From 9ffe2f3ef245034a2dae42017a9059dfe4d02647 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Thu, 11 Mar 2021 17:56:03 -0500 Subject: MarkDown -> Markdown (#1748) * #include an absolute path didn't work - because paths were taken to always be relative. * MarkDown -> Markdown slang-doc-mark-down -> slang-doc-markdown-writer --- source/slang/slang-doc-markdown-writer.cpp | 1114 ++++++++++++++++++++++++++++ 1 file changed, 1114 insertions(+) create mode 100644 source/slang/slang-doc-markdown-writer.cpp (limited to 'source/slang/slang-doc-markdown-writer.cpp') diff --git a/source/slang/slang-doc-markdown-writer.cpp b/source/slang/slang-doc-markdown-writer.cpp new file mode 100644 index 000000000..259762e88 --- /dev/null +++ b/source/slang/slang-doc-markdown-writer.cpp @@ -0,0 +1,1114 @@ +// slang-doc-markdown-writer.cpp +#include "slang-doc-markdown-writer.h" + +#include "../core/slang-string-util.h" + +#include "slang-ast-builder.h" +#include "slang-lookup.h" + +namespace Slang { + +struct DocMarkdownWriter::StringListSet +{ + Index add(const HashSet& set) + { + // -1 means empty, which we don't explicitly store + Index index = -1; + if (set.Count()) + { + List values; + for (auto& value : set) + { + values.add(value); + } + // Sort so that can be compared for uniqueness + values.sort(); + + index = m_uniqueValues.indexOf(values); + if (index < 0) + { + index = m_uniqueValues.getCount(); + m_uniqueValues.add(values); + } + } + m_map.add(index); + return index; + } + + Index getValueIndex(Index index) const { return m_map[index]; } + List& getValuesAt(Index valueIndex) { return m_uniqueValues[valueIndex]; } + const List>& getUniqueValues() const { return m_uniqueValues; } + + StringListSet() {} + +protected: + List m_map; + List> m_uniqueValues; +}; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DocMarkDownWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +template +static void _getDecls(ContainerDecl* containerDecl, List& out) +{ + for (Decl* decl : containerDecl->members) + { + if (T* declAsType = as(decl)) + { + out.add(declAsType); + } + } +} + +template +static void _getDeclsOfType(ContainerDecl* containerDecl, List& out) +{ + for (Decl* decl : containerDecl->members) + { + if (as(decl)) + { + out.add(decl); + } + } +} + +template +static void _toList(FilteredMemberList& list, List& out) +{ + for (Decl* decl : list) + { + out.add(decl); + } +} + +static void _appendAsSingleLine(const UnownedStringSlice& in, StringBuilder& out) +{ + List lines; + StringUtil::calcLines(in, lines); + + // Ideally we'd remove any extraneous whitespace, but for now just join + StringUtil::join(lines.getBuffer(), lines.getCount(), ' ', out); +} + +void DocMarkdownWriter::_appendAsBullets(const List& values, char wrapChar) +{ + auto& out = m_builder; + for (const auto& value : values) + { + out << "* "; + + const String& name = value.name; + if (name.getLength()) + { + if (wrapChar) + { + out.appendChar(wrapChar); + out << name; + out.appendChar(wrapChar); + } + else + { + out << name; + } + } + + if (value.text.getLength()) + { + out.appendChar(' '); + + // Hmm, we'll want to make something multiline into a single line + _appendAsSingleLine(value.text.getUnownedSlice(), out); + } + + out << "\n"; + } +} + +void DocMarkdownWriter::_appendAsBullets(const List& values, char wrapChar) +{ + auto& out = m_builder; + for (const auto& value : values) + { + out << "* "; + + if (value.getLength()) + { + if (wrapChar) + { + out.appendChar(wrapChar); + out << value; + out.appendChar(wrapChar); + + } + else + { + out << value; + } + } + out << "\n"; + } +} + +String DocMarkdownWriter::_getName(Decl* decl) +{ + StringBuilder buf; + ASTPrinter::appendDeclName(decl, buf); + return buf.ProduceString(); +} + +String DocMarkdownWriter::_getName(InheritanceDecl* decl) +{ + StringBuilder buf; + buf.Clear(); + buf << decl->base; + return buf.ProduceString(); +} + +DocMarkdownWriter::NameAndText DocMarkdownWriter::_getNameAndText(DocMarkup::Entry* entry, Decl* decl) +{ + NameAndText nameAndText; + + nameAndText.name = _getName(decl); + + // We could extract different text here, but for now just do all markup + if (entry) + { + // For now we'll just use all markup, but really we need something more sophisticated here + nameAndText.text = entry->m_markup; + } + + return nameAndText; +} + +DocMarkdownWriter::NameAndText DocMarkdownWriter::_getNameAndText(Decl* decl) +{ + DocMarkup::Entry* entry = m_markup->getEntry(decl); + return _getNameAndText(entry, decl); +} + +List DocMarkdownWriter::_getAsNameAndTextList(const List& in) +{ + List out; + for (auto decl : in) + { + out.add(_getNameAndText(decl)); + } + return out; +} + +List DocMarkdownWriter::_getAsStringList(const List& in) +{ + List strings; + for (auto decl : in) + { + strings.add(_getName(decl)); + } + return strings; +} + +void DocMarkdownWriter::_appendCommaList(const List& strings, char wrapChar) +{ + for (Index i = 0; i < strings.getCount(); ++i) + { + if (i > 0) + { + m_builder << toSlice(", "); + } + if (wrapChar) + { + m_builder.appendChar(wrapChar); + m_builder << strings[i]; + m_builder.appendChar(wrapChar); + } + else + { + m_builder << strings[i]; + } + } +} + +/* static */void DocMarkdownWriter::getSignature(const List& parts, Signature& outSig) +{ + const Index count = parts.getCount(); + for (Index i = 0; i < count; ++i) + { + const auto& part = parts[i]; + switch (part.type) + { + case Part::Type::ParamType: + { + PartPair pair; + pair.first = part; + if ((i + 1) < count && 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; + } + case Part::Type::GenericParamValue: + case Part::Type::GenericParamType: + { + Signature::GenericParam genericParam; + genericParam.name = part; + + if ((i + 1) < count && parts[i + 1].type == Part::Type::GenericParamValueType) + { + genericParam.type = parts[i + 1]; + i++; + } + + outSig.genericParams.add(genericParam); + break; + } + + default: break; + } + } +} + +void DocMarkdownWriter::writeVar(const DocMarkup::Entry& entry, VarDecl* varDecl) +{ + writePreamble(entry); + auto& out = m_builder; + + out << toSlice("# ") << varDecl->getName()->text << toSlice("\n\n"); + + // TODO(JS): The outputting of types this way isn't right - it doesn't handle int a[10] for example. + //ASTPrinter printer(m_astBuilder, ASTPrinter::OptionFlag::ParamNames); + + out << toSlice("```\n"); + out << varDecl->type << toSlice(" ") << varDecl << toSlice("\n"); + out << toSlice("```\n\n"); + + writeDescription(entry); +} + +void DocMarkdownWriter::writeSignature(CallableDecl* callableDecl) +{ + auto& out = m_builder; + + List parts; + + ASTPrinter printer(m_astBuilder, ASTPrinter::OptionFlag::ParamNames, &parts); + printer.addDeclSignature(DeclRef(callableDecl, nullptr)); + + Signature signature; + getSignature(parts, signature); + + const Index paramCount = signature.params.getCount(); + + { + // Some types (like constructors say) don't have any return type, so check before outputting + const UnownedStringSlice returnType = printer.getPartSlice(signature.returnType); + if (returnType.getLength() > 0) + { + out << returnType << toSlice(" "); + } + } + + out << printer.getPartSlice(signature.name); + +#if 0 + if (signature.genericParams.getCount()) + { + out << toSlice("<"); + const Index count = signature.genericParams.getCount(); + for (Index i = 0; i < count; ++i) + { + const auto& genericParam = signature.genericParams[i]; + if (i > 0) + { + out << toSlice(", "); + } + out << printer.getPartSlice(genericParam.name); + + if (genericParam.type.type != Part::Type::None) + { + out << toSlice(" : "); + out << printer.getPartSlice(genericParam.type); + } + } + out << toSlice(">"); + } +#endif + + switch (paramCount) + { + case 0: + { + // Has no parameters + out << toSlice("();\n"); + break; + } + case 1: + { + // Place all on single line + out.appendChar('('); + const auto& param = signature.params[0]; + out << printer.getPartSlice(param.first) << toSlice(" ") << printer.getPartSlice(param.second); + out << ");\n"; + break; + } + default: + { + // Put each parameter on a line on it's own + 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"; + break; + } + } +} + +List DocMarkdownWriter::_getUniqueParams(const List& decls) +{ + List out; + + Dictionary nameDict; + + for (auto decl : decls) + { + Name* name = decl->getName(); + if (!name) + { + continue; + } + + Index index = nameDict.GetOrAddValue(name, out.getCount()); + + if (index >= out.getCount()) + { + out.add(NameAndText{ getText(name), String() }); + } + + NameAndText& nameAndMarkup = out[index]; + if (nameAndMarkup.text.getLength() > 0) + { + continue; + } + + auto entry = m_markup->getEntry(decl); + if (entry && entry->m_markup.getLength()) + { + nameAndMarkup.text = entry->m_markup; + } + } + + return out; +} + +static void _addRequirements(Decl* decl, HashSet& outRequirements) +{ + StringBuilder buf; + + if (auto spirvRequiredModifier = decl->findModifier()) + { + buf.Clear(); + buf << "SPIR-V "; + spirvRequiredModifier->version.append(buf); + outRequirements.Add(buf); + } + + if (auto glslRequiredModifier = decl->findModifier()) + { + buf.Clear(); + buf << "GLSL" << glslRequiredModifier->versionNumberToken.getContent(); + outRequirements.Add(buf); + } + + if (auto cudaSMVersionModifier = decl->findModifier()) + { + buf.Clear(); + buf << "CUDA SM "; + cudaSMVersionModifier->version.append(buf); + outRequirements.Add(buf); + } + + if (auto extensionModifier = decl->findModifier()) + { + buf.Clear(); + buf << "GLSL " << extensionModifier->extensionNameToken.getContent(); + outRequirements.Add(buf); + } + + if (auto requiresNVAPIAttribute = decl->findModifier()) + { + outRequirements.Add("NVAPI"); + } +} + +void DocMarkdownWriter::_maybeAppendSet(const UnownedStringSlice& title, const StringListSet& set) +{ + auto& out = m_builder; + + const auto& uniqueValues = set.getUniqueValues(); + + const Index uniqueCount = uniqueValues.getCount(); + if (uniqueCount > 0 && uniqueValues[0].getCount() > 0) + { + out << title; + if (uniqueCount > 1) + { + for (Index i = 0; i < uniqueCount; ++i) + { + out << (i + 1) << (". "); + _appendCommaList(uniqueValues[i], '`'); + out << toSlice("\n"); + } + } + else + { + _appendCommaList(uniqueValues[0], '`'); + out << toSlice("\n"); + } + out << toSlice("\n"); + } +} + +static Decl* _getSameNameDecl(Decl* decl) +{ + auto parentDecl = decl->parentDecl; + + // Sanity check: there should always be a parent declaration. + // + SLANG_ASSERT(parentDecl); + if (!parentDecl) return nullptr; + + // If the declaration is the "inner" declaration of a generic, + // then we actually want to look one level up, because the + // peers/siblings of the declaration will belong to the same + // parent as the generic, not to the generic. + // + if (auto genericParentDecl = as(parentDecl)) + { + // Note: we need to check here to be sure `newDecl` + // is the "inner" declaration and not one of the + // generic parameters, or else we will end up + // checking them at the wrong scope. + // + if (decl == genericParentDecl->inner) + { + decl = parentDecl; + } + } + + return decl; +} + +static bool _isFirstOverridden(Decl* decl) +{ + decl = _getSameNameDecl(decl); + + ContainerDecl* parentDecl = decl->parentDecl; + + // Make sure we have the member dictionary. + buildMemberDictionary(parentDecl); + + Name* declName = decl->getName(); + if (declName) + { + Decl** firstDeclPtr = parentDecl->memberDictionary.TryGetValue(declName); + return (firstDeclPtr && *firstDeclPtr == decl) || (firstDeclPtr == nullptr); + } + + return false; +} + +void DocMarkdownWriter::writeCallableOverridable(const DocMarkup::Entry& entry, CallableDecl* callableDecl) +{ + auto& out = m_builder; + + writePreamble(entry); + + { + // Output the overridable path (ie without terminal generic parameters) + ASTPrinter printer(m_astBuilder); + printer.addOverridableDeclPath(DeclRef(callableDecl, nullptr)); + // Extract the name + out << toSlice("# `") << printer.getStringBuilder() << toSlice("`\n\n"); + } + + writeDescription(entry); + + List sigs; + { + Decl* sameNameDecl = _getSameNameDecl(callableDecl); + + for (Decl* curDecl = sameNameDecl; curDecl; curDecl = curDecl->nextInContainerWithSameName) + { + CallableDecl* sig = nullptr; + if (GenericDecl* genericDecl = as(curDecl)) + { + sig = as(genericDecl->inner); + } + else + { + sig = as(curDecl); + } + + if (!sig) + { + continue; + } + + // Want to add only the primary sig + if (sig->primaryDecl == nullptr || sig->primaryDecl == sig) + { + sigs.add(sig); + } + } + + // Lets put back into source order + sigs.sort([](CallableDecl* a, CallableDecl* b) -> bool { return a->loc.getRaw() < b->loc.getRaw(); }); + } + + // Set of sets of requirements, holds index map from addition to the set entry + StringListSet requirementsSetSet; + + // Similarly for targets + StringListSet targetSetSet; + + // We want to determine the unique signature, and then the requirements for the signature + { + for (Index i = 0; i < sigs.getCount(); ++i) + { + CallableDecl* sig = sigs[i]; + // Add the requirements for all the different versions + { + HashSet requirementsSet; + HashSet targetSet; + for (CallableDecl* curSig = sig; curSig; curSig = curSig->nextDecl) + { + _addRequirements(sig, requirementsSet); + + // Handle Target info + + for (auto targetIntrinsic : sig->getModifiersOfType()) + { + targetSet.Add(String(targetIntrinsic->targetToken.getContent()).toUpper()); + } + for (auto specializedForTarget : sig->getModifiersOfType()) + { + targetSet.Add(String(specializedForTarget->targetToken.getContent()).toUpper()); + } + } + + requirementsSetSet.add(requirementsSet); + + // TODO(JS): This really isn't right, we ideally have markup that made hlsl availability explicit + // We *assume* that we have 'hlsl' for now + targetSet.Add(String("HLSL")); + targetSetSet.add(targetSet); + } + } + } + + // Output the signature + { + out << toSlice("## Signature \n\n"); + out << toSlice("```\n"); + + Index prevRequirementsIndex = -1; + Index prevTargetIndex = -1; + + const Int sigCount = sigs.getCount(); + for (Index i = 0; i < sigCount; ++i) + { + auto sig = sigs[i]; + + // Output if needs unique requirements + if (requirementsSetSet.getUniqueValues().getCount() > 1) + { + const Index requirementsIndex = requirementsSetSet.getValueIndex(i); + if (requirementsIndex != prevRequirementsIndex) + { + if (requirementsIndex >= 0) + { + out << toSlice("/// See Requirement ") << (requirementsIndex + 1) << toSlice("\n"); + } + else + { + out << toSlice("/// No requirements\n"); + } + prevRequirementsIndex = requirementsIndex; + } + } + + if (targetSetSet.getUniqueValues().getCount() > 1) + { + const Index targetIndex = targetSetSet.getValueIndex(i); + if (targetIndex != prevTargetIndex) + { + if (targetIndex >= 0) + { + out << toSlice("/// See Target Availability ") << (targetIndex + 1) << toSlice("\n"); + } + else + { + out << toSlice("/// All Targets\n"); + } + prevTargetIndex = targetIndex; + } + } + + writeSignature(sig); + } + out << "```\n\n"; + } + + _maybeAppendSet(toSlice("## Requirements\n\n"), requirementsSetSet); + + // Target availability + + { + const auto& uniqueValues = targetSetSet.getUniqueValues(); + if (uniqueValues.getCount() == 1 && uniqueValues[0].getCount() == 1 && uniqueValues[0][0] == "hlsl") + { + // TODO(JS): + // If something is marked up for hlsl, and nothing else, that indicates it might *only* be available on hlsl, but + // we don't correctly handle that here. + + // If the only thing we have is 'hlsl' - we injected that so we'll *assume* it's available everywhere, so + // don't bother outputting. + } + else + { + _maybeAppendSet(toSlice("## Target Availability\n\n"), targetSetSet); + } + } + + { + // We will use the first documentation found for each parameter type + { + List paramDecls; + List genericDecls; + for (auto sig : sigs) + { + GenericDecl* genericDecl = as(sig->parentDecl); + + // NOTE! + // Here we assume the names of generic parameters are such that they are + + // We list generic parameters, as types of parameters, if they are directly associated with this + // callable. + if (genericDecl) + { + for (Decl* decl : genericDecl->members) + { + if (as(decl) || + as(decl)) + { + genericDecls.add(decl); + } + } + } + + for (ParamDecl* paramDecl : sig->getParameters()) + { + paramDecls.add(paramDecl); + } + } + + if (paramDecls.getCount() > 0 || paramDecls.getCount() > 0) + { + out << "## Parameters\n\n"; + + // Get the unique generics + _appendAsBullets(_getUniqueParams(genericDecls), '`'); + // And parameters + _appendAsBullets(_getUniqueParams(paramDecls), '`'); + } + } + } +} + +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"); + + _appendAsBullets(_getAsNameAndTextList(enumDecl->getMembersOfType()), '_'); + + writeDescription(entry); +} + +void DocMarkdownWriter::_appendEscaped(const UnownedStringSlice& text) +{ + auto& out = m_builder; + + const char* start = text.begin(); + const char* cur = start; + const char*const end = text.end(); + + for (; cur < end; ++cur) + { + const char c = *cur; + + switch (c) + { + case '<': + case '>': + case '&': + case '"': + case '_': + { + // Flush if any before + if (cur > start) + { + out.append(start, cur); + } + // Prefix with the + out.appendChar('\\'); + + // Start will still include the char, for later flushing + start = cur; + break; + } + default: break; + } + } + + // Flush any remaining + if (cur > start) + { + out.append(start, cur); + } +} + + +void DocMarkdownWriter::_appendDerivedFrom(const UnownedStringSlice& prefix, AggTypeDeclBase* aggTypeDecl) +{ + auto& out = m_builder; + + List inheritanceDecls; + _getDecls(aggTypeDecl, inheritanceDecls); + + const Index count = inheritanceDecls.getCount(); + if (count) + { + out << prefix; + for (Index i = 0; i < count; ++i) + { + InheritanceDecl* inheritanceDecl = inheritanceDecls[i]; + if (i > 0) + { + out << toSlice(", "); + } + out << inheritanceDecl->base; + } + } +} + +void DocMarkdownWriter::_appendAggTypeName(AggTypeDeclBase* aggTypeDecl) +{ + auto& out = m_builder; + + // This could be lots of different things - struct/class/extension/interface/.. + + ASTPrinter printer(m_astBuilder); + printer.addDeclPath(DeclRef(aggTypeDecl, nullptr)); + + if (as(aggTypeDecl)) + { + out << toSlice("struct ") << printer.getStringBuilder(); + } + else if (as(aggTypeDecl)) + { + out << toSlice("class ") << printer.getStringBuilder(); + } + else if (as(aggTypeDecl)) + { + out << toSlice("interface ") << printer.getStringBuilder(); + } + else if (ExtensionDecl* extensionDecl = as(aggTypeDecl)) + { + out << toSlice("extension ") << extensionDecl->targetType; + _appendDerivedFrom(toSlice(" : "), extensionDecl); + } + else + { + out << toSlice("?"); + } +} + +void DocMarkdownWriter::writeAggType(const DocMarkup::Entry& entry, AggTypeDeclBase* aggTypeDecl) +{ + writePreamble(entry); + + auto& out = m_builder; + + // We can write out he name using the printer + + ASTPrinter printer(m_astBuilder); + printer.addDeclPath(DeclRef(aggTypeDecl, nullptr)); + + out << toSlice("# `"); + _appendAggTypeName(aggTypeDecl); + out << toSlice("`\n\n"); + + { + List inheritanceDecls; + _getDecls(aggTypeDecl, inheritanceDecls); + + if (inheritanceDecls.getCount()) + { + out << "*Implements:* "; + _appendCommaList(_getAsStringList(inheritanceDecls), '`'); + out << toSlice("\n\n"); + } + } + + writeDescription(entry); + + { + List assocTypeDecls; + _getDecls(aggTypeDecl, assocTypeDecls); + + if (assocTypeDecls.getCount()) + { + out << toSlice("# Associated types\n\n"); + + for (AssocTypeDecl* assocTypeDecl : assocTypeDecls) + { + out << "* _" << assocTypeDecl->getName()->text << "_ "; + + // Look up markup + DocMarkup::Entry* assocTypeDeclEntry = m_markup->getEntry(assocTypeDecl); + if (assocTypeDeclEntry) + { + _appendAsSingleLine(assocTypeDeclEntry->m_markup.getUnownedSlice(), out); + } + out << toSlice("\n"); + + List inheritanceDecls; + _getDecls(assocTypeDecl, inheritanceDecls); + + if (inheritanceDecls.getCount()) + { + out << " "; + _appendCommaList(_getAsStringList(inheritanceDecls), '`'); + out << toSlice("\n"); + } + } + + out << toSlice("\n\n"); + } + } + + if (GenericDecl* genericDecl = as(aggTypeDecl->parentDecl)) + { + // The parameters, in order + List params; + for (Decl* decl : genericDecl->members) + { + if (as(decl) || + as(decl)) + { + params.add(decl); + } + } + + if (params.getCount()) + { + out << "## Generic Parameters\n\n"; + _appendAsBullets(_getAsNameAndTextList(params), '`'); + } + } + + { + List fields; + _getDeclsOfType(aggTypeDecl, fields); + if (fields.getCount()) + { + out << "## Fields\n\n"; + + _appendAsBullets(_getAsNameAndTextList(fields), '`'); + } + } + + { + // Make sure we've got a query-able member dictionary + buildMemberDictionary(aggTypeDecl); + SLANG_ASSERT(aggTypeDecl->isMemberDictionaryValid()); + + List uniqueMethods; + for (const auto& pair : aggTypeDecl->memberDictionary) + { + CallableDecl* callableDecl = as(pair.Value); + if (callableDecl && isVisible(callableDecl)) + { + uniqueMethods.add(callableDecl); + } + } + + if (uniqueMethods.getCount()) + { + // Put in source definition order + uniqueMethods.sort([](Decl* a, Decl* b) -> bool { return a->loc.getRaw() < b->loc.getRaw(); }); + + out << "## Methods\n\n"; + _appendAsBullets(_getAsStringList(uniqueMethods), '`'); + } + } +} + +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; + + if (entry.m_markup.getLength() > 0) + { + out << toSlice("## Description\n\n"); + + out << entry.m_markup.getUnownedSlice(); +#if 0 + UnownedStringSlice text(entry.m_markup.getUnownedSlice()), line; + while (StringUtil::extractLine(text, line)) + { + out << line << toSlice("\n"); + } +#endif + out << toSlice("\n"); + } +} + +void DocMarkdownWriter::writeDecl(const DocMarkup::Entry& entry, Decl* decl) +{ + // Skip these they will be output as part of their respective 'containers' + if (as(decl) || as(decl) || as(decl) || as(decl)) + { + return; + } + + if (CallableDecl* callableDecl = as(decl)) + { + if (_isFirstOverridden(callableDecl)) + { + writeCallableOverridable(entry, callableDecl); + } + } + else if (EnumDecl* enumDecl = as(decl)) + { + writeEnum(entry, enumDecl); + } + else if (AggTypeDeclBase* aggType = as(decl)) + { + writeAggType(entry, aggType); + } + else if (VarDecl* varDecl = as(decl)) + { + // If part of aggregate type will be output there. + if (as(varDecl->parentDecl)) + { + return; + } + + writeVar(entry, varDecl); + } + else if (as(decl)) + { + // We can ignore as inner decls will be picked up, and written + } +} + +bool DocMarkdownWriter::isVisible(const Name* name) +{ + return name == nullptr || !name->text.startsWith(toSlice("__")); +} + +bool DocMarkdownWriter::isVisible(const DocMarkup::Entry& entry) +{ + // For now if it's not public it's not visible + if (entry.m_visibility != MarkupVisibility::Public) + { + return false; + } + + Decl* decl = as(entry.m_node); + return decl == nullptr || isVisible(decl->getName()); +} + +bool DocMarkdownWriter::isVisible(Decl* decl) +{ + if (!isVisible(decl->getName())) + { + return false; + } + + auto entry = m_markup->getEntry(decl); + return entry == nullptr || entry->m_visibility == MarkupVisibility::Public; +} + +void DocMarkdownWriter::writeAll() +{ + for (const auto& entry : m_markup->getEntries()) + { + Decl* decl = as(entry.m_node); + + if (decl && isVisible(entry)) + { + writeDecl(entry, decl); + } + } +} + +} // namespace Slang -- cgit v1.2.3