MBDocGen_CLI.mbd
#include "MBDocGen_CLI.h"
#include "MBLSP/LSP_Structs.h"
#include "MBUtility/MBStrings.h"
#include <MBCLI/MBCLI.h>
#include <MBUtility/MBFiles.h>
#include <MBSystem/MBSystem.h>
#include <MBDoc/MBDoc.h>
#include <MBPacketManager/MBBuild/MBBuild.h>
#include <MBSystem/BiDirectionalSubProcess.h>
#include <MBUtility/InterfaceAdaptors.h>
namespace MBDocGen
{
int DocGenCLI::Run(MBCLI::MBTerminal& AssociatedTerminal,int argc, const char* const* argv)
{
int ReturnValue = 0;
MBCLI::ArgumentListCLIInput Command(argc,argv);
try
{
std::string OutPath;
auto OutPathIt = Command.CommandArgumentOptions.find("o");
if(OutPathIt == Command.CommandArgumentOptions.end())
{
AssociatedTerminal.PrintLine("Mandatory to include path to output directory with the o argument option");
return(1);
}
if(OutPathIt->second.size() > 1)
{
AssociatedTerminal.PrintLine("Only one output directory may be specified");
return(1);
}
OutPath = OutPathIt->second[0];
if(Command.CommandArguments.size() == 0)
{
AssociatedTerminal.PrintLine("the path to the MBSourceInfo.json file is required to the the first argument");
return(1);
}
std::filesystem::path MBSourceInfoPath = Command.CommandArguments[0];
if(!std::filesystem::exists(MBSourceInfoPath))
{
AssociatedTerminal.PrintLine("Cannot find the path to the MBSourceInfo.json file");
return(1);
}
std::string SourceInfoData = MBUtility::ReadWholeFile(Command.CommandArguments[0]);
MBPM::MBBuild::SourceInfo InfoToInspect;
MBError ParseError = MBPM::MBBuild::ParseSourceInfo(SourceInfoData.data(),SourceInfoData.size(),InfoToInspect);
if(!ParseError)
{
AssociatedTerminal.PrintLine("Error parsing sourceinfo: "+ParseError.ErrorMessage);
return(1);
}
if(!(InfoToInspect.Language == "C++" || InfoToInspect.Language == "C"))
{
AssociatedTerminal.PrintLine("Only C++ and C is currently supported");
return(1);
}
CppDocGenerator CPPGenerator;
CPPGenerator.GenerateDoc(AssociatedTerminal,std::filesystem::absolute(MBSourceInfoPath).parent_path(),InfoToInspect,OutPath);
}
catch(std::exception const& e)
{
AssociatedTerminal.PrintLine("Uncaught exception executing command: "+std::string(e.what()));
ReturnValue = 1;
}
return(ReturnValue);
}
bool h_PathIsPrefix(std::filesystem::path const& PrefixPath,std::filesystem::path const& SubPath)
{
bool ReturnValue = true;
std::string RelativePath = MBUnicode::PathToUTF8(std::filesystem::relative(SubPath,PrefixPath));
if(RelativePath.size() >= 2 && (RelativePath[0] == '.' && RelativePath[1] == '.'))
{
return(false);
}
return(ReturnValue);
}
MBDoc::TextElement CppDocGenerator::p_RefNodeToTextElement(ASTNode const& NodeToConvert)
{
MBDoc::TextElement ReturnValue;
if(NodeToConvert.range)
{
GotoDefinition_Request Req;
Req.params.textDocument.uri = m_CurrentURI;
Req.params.position = NodeToConvert.range->start;
GotoDefinition_Response Response = m_AssociatedLSP->SendRequest(Req);
if(Response.result)
{
if(Response.result->size() == 1)
{
try
{
MBDoc::UnresolvedReference Ref;
Ref.Color = MBDoc::TextColor(0,255,0);
Ref.VisibleText = NodeToConvert.detail.Value();
std::filesystem::path DefinitionFile = MBLSP::URLDecodePath(Response.result->at(0).uri);
if(h_PathIsPrefix(m_ProjectRoot,DefinitionFile))
{
Ref.ReferenceString = "../{"+Ref.VisibleText+".mbd}";
return(Ref);
}
else if(!MBPM_PACKETS_INSTALL_DIRECTORY.empty() && h_PathIsPrefix(MBPM_PACKETS_INSTALL_DIRECTORY,DefinitionFile))
{
std::string RelativePath = MBUnicode::PathToUTF8(std::filesystem::relative(DefinitionFile,MBPM_PACKETS_INSTALL_DIRECTORY));
std::replace(RelativePath.begin(), RelativePath.end(), '\\', '/');
size_t DirectoryEnd = RelativePath.find('/');
if(DirectoryEnd == RelativePath.npos)
{
DirectoryEnd = RelativePath.size();
}
Ref.ReferenceString = "/{";
Ref.ReferenceString.insert(Ref.ReferenceString.end(),RelativePath.begin(),RelativePath.begin()+DirectoryEnd);
Ref.ReferenceString += "}/{Code/";
Ref.ReferenceString += "Classes";
Ref.ReferenceString += "}/"+Ref.VisibleText+".mbd";
return(Ref);
}
}
catch(std::exception const& e) { }
}
}
}
MBDoc::RegularText NewText;
NewText.Color = MBDoc::TextColor(0,255,0);
NewText.Text = NodeToConvert.detail.Value();
ReturnValue = NewText;
return(ReturnValue);
}
std::vector<MBDoc::TextElement> CppDocGenerator::p_ParseType(ASTNode const& NodeToConvert)
{
std::vector<MBDoc::TextElement> ReturnValue;
if(NodeToConvert.kind == "Elaborated")
{
for(auto const& Child : NodeToConvert.children.Value())
{
std::vector<MBDoc::TextElement> NewContent = p_ParseType(Child);
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(NewContent.begin()),std::make_move_iterator(NewContent.end()));
}
}
else if(NodeToConvert.kind == "Namespace")
{
MBDoc::RegularText NewText;
NewText.Color = MBDoc::TextColor(255,255,255);
NewText.Text = NodeToConvert.detail.Value();
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "TemplateSpecialization")
{
std::vector<MBDoc::TextElement> TemplateName = p_ParseType(NodeToConvert.children->at(0));
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(TemplateName.begin()),std::make_move_iterator(TemplateName.end()));
MBDoc::RegularText NewText;
NewText.Text = "<";
ReturnValue.push_back(NewText);
for(int i = 1; i < NodeToConvert.children->size();i++)
{
std::vector<MBDoc::TextElement> NewContent = p_ParseType(NodeToConvert.children->at(i));
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(NewContent.begin()),std::make_move_iterator(NewContent.end()));
if (i + 1 < NodeToConvert.children->size())
{
MBDoc::RegularText NewText;
NewText.Text = ",";
ReturnValue.push_back(std::move(NewText));
}
}
NewText.Text = ">";
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "Typedef")
{
MBDoc::TextElement NewText = p_RefNodeToTextElement(NodeToConvert);
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "Record")
{
MBDoc::TextElement NewText = p_RefNodeToTextElement(NodeToConvert);
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "LValueReference")
{
std::vector<MBDoc::TextElement> NewContent = p_ParseType(NodeToConvert.children->at(0));
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(NewContent.begin()),std::make_move_iterator(NewContent.end()));
MBDoc::RegularText NewText;
NewText.Text = "\\&";
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "Pointer")
{
std::vector<MBDoc::TextElement> NewContent = p_ParseType(NodeToConvert.children->at(0));
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(NewContent.begin()),std::make_move_iterator(NewContent.end()));
MBDoc::RegularText NewText;
NewText.Text = "\\*";
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "Qualified")
{
MBDoc::RegularText NewText;
NewText.Text = "const ";
ReturnValue.push_back(std::move(NewText));
std::vector<MBDoc::TextElement> NewContent = p_ParseType(NodeToConvert.children->at(0));
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(NewContent.begin()),std::make_move_iterator(NewContent.end()));
}
else if(NodeToConvert.kind == "Builtin")
{
MBDoc::RegularText NewText;
NewText.Color = MBDoc::TextColor(0,0,255);
NewText.Text = NodeToConvert.detail.Value();
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.role == "template argument")
{
ReturnValue = p_ParseType(NodeToConvert.children->at(0));
}
else if(NodeToConvert.kind == "Template")
{
MBDoc::TextElement NewText = p_RefNodeToTextElement(NodeToConvert);
ReturnValue.push_back(std::move(NewText));
}
else if(NodeToConvert.kind == "Enum")
{
MBDoc::TextElement NewText = p_RefNodeToTextElement(NodeToConvert);
ReturnValue.push_back(std::move(NewText));
}
return(ReturnValue);
}
void CppDocGenerator::p_ParseFieldDeclaration(ASTNode const& NodeToConvert,std::unordered_map<std::string,std::vector<MBDoc::TextElement>>& MapToUpdate)
{
std::string FieldName = NodeToConvert.detail.Value();
MBDoc::UnresolvedReference Ref;
Ref.VisibleText = FieldName;
Ref.ReferenceString = "#"+FieldName;
std::vector<MBDoc::TextElement>& ContentToUpdate = MapToUpdate[FieldName];
std::vector<MBDoc::TextElement> Type = p_ParseType(NodeToConvert.children->at(0));
ContentToUpdate.insert(ContentToUpdate.end(),std::make_move_iterator(Type.begin()),std::make_move_iterator(Type.end()));
MBDoc::RegularText Space;
Space.Text = " ";
ContentToUpdate.push_back(Space);
ContentToUpdate.push_back(Ref);
}
void CppDocGenerator::p_ParseFunctionDeclaration(ASTNode const& NodeToConvert,std::unordered_map<std::string,std::vector<std::vector<MBDoc::TextElement>>>& MapToUpdate)
{
if (!NodeToConvert.children || NodeToConvert.children->size() == 0)
{
return;
}
ASTNode const& FunctionProto = NodeToConvert.children->at(0);
std::vector<MBDoc::TextElement> ReturnType = p_ParseType(FunctionProto.children.Value()[0]);
std::string FunctionName = NodeToConvert.detail.Value();
assert(FunctionName[0] != ' ');
MBDoc::UnresolvedReference FunctionNameReference;
int FunctionIndex = MapToUpdate[FunctionName].size();
FunctionNameReference.Color = MBDoc::TextColor(255,255,0);
FunctionNameReference.VisibleText = FunctionName;
FunctionNameReference.ReferenceString = "#"+FunctionName+"_"+std::to_string(FunctionIndex);
std::vector<MBDoc::TextElement> NewText;
NewText.insert(NewText.end(),std::make_move_iterator(ReturnType.begin()),std::make_move_iterator(ReturnType.end()));
MBDoc::RegularText Space;
Space.Text = " ";
NewText.push_back(Space);
NewText.push_back(FunctionNameReference);
MBDoc::RegularText Paranthes;
Paranthes.Text = "(";
NewText.push_back(Paranthes);
for(int i = 1; i < FunctionProto.children.Value().size();i++)
{
std::vector<MBDoc::TextElement> Param = p_ParseFunctionArgument(FunctionNameReference.ReferenceString,FunctionProto.children.Value()[i]);
NewText.insert(NewText.end(),std::make_move_iterator(Param.begin()),std::make_move_iterator(Param.end()));
if(i +1 < FunctionProto.children.Value().size())
{
MBDoc::RegularText Comma;
Comma.Text = ",";
NewText.push_back(std::move(Comma));
}
}
Paranthes.Text = ")";
NewText.push_back(Paranthes);
MapToUpdate[FunctionName].push_back(std::move(NewText));
}
std::vector<MBDoc::TextElement> CppDocGenerator::p_ParseFunctionArgument(std::string const& FunctionName,ASTNode const& NodeToConvert)
{
std::vector<MBDoc::TextElement> ReturnValue;
MBDoc::UnresolvedReference Ref;
Ref.Color = MBDoc::TextColor(0xaf,0xee,0xee);
Ref.VisibleText = NodeToConvert.detail.Value();
Ref.ReferenceString = FunctionName+"#"+NodeToConvert.detail.Value();
std::vector<MBDoc::TextElement> ParamType = p_ParseType(NodeToConvert.children->at(0));
ReturnValue.insert(ReturnValue.end(),std::make_move_iterator(ParamType.begin()),std::make_move_iterator(ParamType.end()));
MBDoc::RegularText Space;
Space.Text = " ";
ReturnValue.push_back(Space);
ReturnValue.push_back(std::move(Ref));
return(ReturnValue);
}
void CppDocGenerator::p_ParseClassDefinition(ASTNode const& NodeToConvert,std::unordered_map<std::string,CppClass>& MapToUpdate)
{
std::string ClassName = NodeToConvert.detail.Value();
CppClass& ClassToUpdate = MapToUpdate[ClassName];
CppClassContent* ContentToUpdate = &ClassToUpdate.PrivateContent;
if(NodeToConvert.arcana && NodeToConvert.arcana->find("struct") != NodeToConvert.arcana->npos)
{
ContentToUpdate = &ClassToUpdate.PublicContent;
}
if(NodeToConvert.children)
{
for(auto const& Child : NodeToConvert.children.Value())
{
if(Child.kind == "CXXMethod")
{
p_ParseFunctionDeclaration(Child,ContentToUpdate->Methods);
}
else if(Child.kind == "Field")
{
p_ParseFieldDeclaration(Child,ContentToUpdate->Variables);
}
else if(Child.kind == "AccessSpec")
{
if(Child.arcana)
{
if(Child.arcana->find("public") != Child.arcana->npos)
{
ContentToUpdate = &ClassToUpdate.PublicContent;
}
else if(Child.arcana->find("protected") != Child.arcana->npos)
{
ContentToUpdate = &ClassToUpdate.ProtectedContent;
}
else if(Child.arcana->find("private") != Child.arcana->npos)
{
ContentToUpdate = &ClassToUpdate.PrivateContent;
}
}
}
}
}
}
void CppDocGenerator::p_ParseNamespaceContent(ASTNode const& AST,CppHeaderContent& ContentToUpdate)
{
if(AST.children)
{
for(auto const& Child : AST.children.Value())
{
if(Child.kind == "Namespace")
{
p_ParseNamespaceContent(Child,ContentToUpdate);
}
else if(Child.kind == "CXXRecord")
{
p_ParseClassDefinition(Child,ContentToUpdate.Classes);
}
else if(Child.kind == "Function")
{
p_ParseFunctionDeclaration(Child,ContentToUpdate.Functions);
}
}
}
}
CppHeaderContent CppDocGenerator::p_ASTToHeaderContent(MBLSP::LSP_Client& AssociatedClient, ASTNode const& AST)
{
CppHeaderContent ReturnValue;
p_ParseNamespaceContent(AST,ReturnValue);
return(ReturnValue);
}
std::unique_ptr<MBLSP::LSP_Client> CppDocGenerator::p_StartClangd(std::filesystem::path const& ProjectDir)
{
std::unique_ptr<MBLSP::LSP_Client> ReturnValue;
std::unique_ptr<MBSystem::BiDirectionalSubProcess> Processs = std::make_unique<MBSystem::BiDirectionalSubProcess>("clangd",std::vector<std::string>());
InitializeRequest InitReq;
InitReq.params.rootUri = MBLSP::URLEncodePath(std::filesystem::absolute(ProjectDir));
InitReq.params.rootPath = MBUnicode::PathToUTF8(std::filesystem::absolute(ProjectDir));
std::cout<<"ROOTURI: "<<MBLSP::URLEncodePath(std::filesystem::absolute(ProjectDir))<<std::endl;
std::cout<<"rootpath: "<<InitReq.params.rootPath.Value()<<std::endl;
InitReq.params.initializationOptions = MBParsing::JSONObject(MBParsing::JSONObjectType::Aggregate);
std::string ExtraFlag;
const char* MBPM_PACKETS_INSTALL_DIRECTORY = std::getenv("MBPM_PACKETS_INSTALL_DIRECTORY");
if(MBPM_PACKETS_INSTALL_DIRECTORY != nullptr)
{
ExtraFlag = "-I"+std::string(MBPM_PACKETS_INSTALL_DIRECTORY);
}
InitReq.params.initializationOptions.Value()["fallbackFlags"] = std::vector<std::string>{ExtraFlag,"-std=c++17"};
std::unique_ptr<MBUtility::NonOwningOutputStream> OutStream = std::make_unique<MBUtility::NonOwningOutputStream>(Processs.get());
ReturnValue = std::make_unique<MBLSP::LSP_Client>(std::move(Processs),std::move(OutStream));
ReturnValue->InitializeServer(InitReq);
return(ReturnValue);
}
int CppDocGenerator::GenerateDoc(MBCLI::MBTerminal& AssociatedTerminal,
std::filesystem::path const& ProjectRoot,
MBPM::MBBuild::SourceInfo const& SourceInfo,
std::filesystem::path const& OutDirectory)
{
int ReturnValue = 0;
m_ProjectRoot = ProjectRoot;
if(const char* MBPM_PACKETS_INSTALL_DIRECTORY_Env = std::getenv("MBPM_PACKETS_INSTALL_DIRECTORY"); MBPM_PACKETS_INSTALL_DIRECTORY_Env != nullptr)
{
MBPM_PACKETS_INSTALL_DIRECTORY = MBPM_PACKETS_INSTALL_DIRECTORY_Env;
}
try
{
m_AssociatedLSP = p_StartClangd(ProjectRoot);
std::vector<std::string> TotalSources = GetAllSources(SourceInfo);
for(auto const& Source : TotalSources)
{
std::filesystem::path OutPath = (OutDirectory/"Code/Sources"/Source.substr(1)).replace_extension("mbd");
if(!std::filesystem::exists(OutPath.parent_path()))
{
std::filesystem::create_directories(OutPath.parent_path());
}
std::ofstream OutStream = std::ofstream(OutPath,std::ios::out|std::ios::binary);
if(!OutStream.is_open())
{
AssociatedTerminal.PrintLine("Can't open output file '"+MBUnicode::PathToUTF8(OutPath)+"'");
return(1);
}
OutStream<<"%title "<<MBUnicode::PathToUTF8(OutPath.filename())<<"\n";
OutStream<<"```cpp\n";
std::ifstream InStream = std::ifstream(ProjectRoot/Source.substr(1),std::ios::in|std::ios::binary);
if(!InStream.is_open())
{
AssociatedTerminal.PrintLine("Can't open input file '"+MBUnicode::PathToUTF8(ProjectRoot/Source.substr(1))+"'");
return(1);
}
char ReadBuf[4096];
while(true)
{
auto ReadBytes = InStream.read(ReadBuf,4096).gcount();
OutStream.write(ReadBuf,ReadBytes);
if(ReadBytes < 4096)
{
break;
}
}
OutStream.write("\n```\n",5);
OutStream.flush();
}
MBPM::MBBuild::ExtraLanguageInfo_Cpp CPPLanguageInfo;
if(SourceInfo.ExtraLanguageInfo.GetType() != MBParsing::JSONObjectType::Null)
{
CPPLanguageInfo = MBPM::MBBuild::ParseExtraLanguageInfo_Cpp(SourceInfo.ExtraLanguageInfo);
for(auto const& Header : CPPLanguageInfo.Headers)
{
std::filesystem::path HeaderPath = ProjectRoot/Header.substr(1);
if(!std::filesystem::exists(HeaderPath))
{
throw std::runtime_error("Cannot find open header at locastion '"+MBUnicode::PathToUTF8(HeaderPath)+"'");
}
DidOpenTextDocument_Notification OpenNotification;
OpenNotification.params.textDocument.languageId = "cpp";
OpenNotification.params.textDocument.text = MBUtility::ReadWholeFile(MBUnicode::PathToUTF8(HeaderPath));
OpenNotification.params.textDocument.uri = MBLSP::URLEncodePath(HeaderPath);
m_AssociatedLSP->SendNotification(OpenNotification);
_AST_Request Request;
Request.params.textDocument.uri = OpenNotification.params.textDocument.uri;
int NewLineCount = std::count(OpenNotification.params.textDocument.text.begin(),OpenNotification.params.textDocument.text.end(),'\n');
Request.params.range.start.line = 0;
Request.params.range.start.character = 0;
Request.params.range.end.line = NewLineCount;
Request.params.range.end.character = 0;
MBParsing::JSONObject Response = m_AssociatedLSP->SendRawRequest(Request.GetJSON());
m_CurrentURI = Request.params.textDocument.uri;
try
{
if(!Response.HasAttribute("error"))
{
if(Response["result"].GetType() == MBParsing::JSONObjectType::Null)
{
AssociatedTerminal.PrintLine("Error retrieving AST for header '"+Header+"': skipping header");
continue;
}
_AST_Response ParsedResponse;
ParsedResponse.FillObject(Response);
CppHeaderContent Content = p_ASTToHeaderContent(*m_AssociatedLSP,ParsedResponse.result.Value());
p_WriteHeaderContent(Content,OutDirectory);
}
else
{
AssociatedTerminal.PrintLine("Error retrieving ast for header '"+MBUnicode::PathToUTF8(HeaderPath)+"': "+Response["error"]["message"].GetStringData());
}
}
catch(std::exception const& e)
{
AssociatedTerminal.PrintLine("Uncaught exception generating documentation from header '"+Header+"': "+std::string(e.what()));
}
}
}
}
catch(std::exception const& e)
{
AssociatedTerminal.PrintLine(e.what());
return(1);
}
return(ReturnValue);
}
void CppDocGenerator::p_WriteTextElements(std::vector<MBDoc::TextElement> const& ElementsToWrite,MBUtility::MBOctetOutputStream& OutStream)
{
for(auto const& Element : ElementsToWrite)
{
if(!Element.GetBase().Color.IsDefault())
{
auto Color = Element.GetBase().Color;
OutStream<<"~"<<MBUtility::HexEncodeByte(Color.R)<<MBUtility::HexEncodeByte(Color.G)<<MBUtility::HexEncodeByte(Color.B)<<" ";
}
if(Element.IsType<MBDoc::RegularText>())
{
OutStream<<Element.GetType<MBDoc::RegularText>().Text;
}
else if(Element.IsType<MBDoc::DocReference>())
{
MBDoc::DocReference const& Ref = Element.GetType<MBDoc::DocReference>();
OutStream<<"@["<<Ref.VisibleText<<"]("<<static_cast<MBDoc::UnresolvedReference const&>(Ref).ReferenceString<<")";
}
if(!Element.GetBase().Color.IsDefault())
{
OutStream<<"~ ";
}
}
OutStream<<"\n";
}
void CppDocGenerator::p_WriteFunctions(std::vector<std::vector<MBDoc::TextElement>> const& FunctionDefinitions,MBUtility::MBOctetOutputStream& OutStream)
{
OutStream<<"//DocGen_Begin\n";
OutStream<<"\\table 1\n";
for(auto const& Function : FunctionDefinitions)
{
p_WriteTextElements(Function,OutStream);
OutStream<<"&";
}
OutStream<<"\n\\end 1\n";
OutStream<<"//DocGen_End\n";
}
void CppDocGenerator::p_WriteClassContent(CppClassContent const& Content,MBUtility::MBOctetOutputStream& OutStream)
{
for(auto const& Member : Content.Variables)
{
p_WriteTextElements(Member.second,OutStream);
OutStream<<"&";
}
for(auto const& Function : Content.Methods)
{
for(auto const& Overload : Function.second)
{
p_WriteTextElements(Overload,OutStream);
OutStream<<"&";
}
}
}
void CppDocGenerator::p_WriteClass(CppClass const& ClassToWrite,MBUtility::MBOctetOutputStream& OutStream)
{
OutStream<<"//DocGen_Begin\n";
OutStream<<"\\table 1\n";
if(!ClassToWrite.PrivateContent.IsEmpty())
{
OutStream<<"Private:&\n";
p_WriteClassContent(ClassToWrite.PrivateContent,OutStream);
}
if(!ClassToWrite.ProtectedContent.IsEmpty())
{
OutStream<<"Protected:&\n";
p_WriteClassContent(ClassToWrite.ProtectedContent,OutStream);
}
if(!ClassToWrite.PublicContent.IsEmpty())
{
OutStream<<"Public:&\n";
p_WriteClassContent(ClassToWrite.PublicContent,OutStream);
}
OutStream<<"\n\\end 1\n";
OutStream<<"//DocGen_End\n";
}
void CppDocGenerator::p_WriteHeaderContent(CppHeaderContent const& ContentToWrite,std::filesystem::path const& OutPath)
{
for(auto const& Function : ContentToWrite.Functions)
{
std::filesystem::path OutFilePath = OutPath/"Code"/"Functions"/(Function.first+".mbd");
if(!std::filesystem::exists(OutFilePath.parent_path()))
{
std::filesystem::create_directories(OutFilePath.parent_path());
}
std::string PrevBodyContent;
if(std::filesystem::exists(OutFilePath))
{
std::string TotalFileData = MBUtility::ReadWholeFile(MBUnicode::PathToUTF8(OutFilePath));
if(TotalFileData.find("//DocGen_Begin") != 0)
{
throw std::runtime_error("Existing output file "+MBUnicode::PathToUTF8(OutFilePath)+
" has invalid DocGen header, file is not overriden");
}
std::string DocGenEnd = "\n//DocGen_End";
size_t EndPosition = TotalFileData.find(DocGenEnd);
if(EndPosition == TotalFileData.npos)
{
throw std::runtime_error("Existing output file "+MBUnicode::PathToUTF8(OutFilePath)+
" has invalid DocGen header, file is not overriden");
}
EndPosition += DocGenEnd.size();
EndPosition = TotalFileData.find('\n',EndPosition);
if(EndPosition == TotalFileData.npos)
{
throw std::runtime_error("Existing output file "+MBUnicode::PathToUTF8(OutFilePath)+
" has invalid DocGen header, file is not overriden");
}
EndPosition += 1;
PrevBodyContent = TotalFileData.substr(EndPosition);
}
std::ofstream OutFileStream = std::ofstream(OutFilePath,std::ios::out|std::ios::binary);
MBUtility::MBFileOutputStream OutStream(&OutFileStream);
p_WriteFunctions(Function.second,OutStream);
OutStream<<PrevBodyContent;
}
for(auto const& Class : ContentToWrite.Classes)
{
std::filesystem::path OutFilePath = OutPath/"Code"/"Classes"/(Class.first+".mbd");
if(!std::filesystem::exists(OutFilePath.parent_path()))
{
std::filesystem::create_directories(OutFilePath.parent_path());
}
std::string PrevBodyContent;
if(std::filesystem::exists(OutFilePath))
{
std::string TotalFileData = MBUtility::ReadWholeFile(MBUnicode::PathToUTF8(OutFilePath));
if(TotalFileData.find("//DocGen_Begin") != 0)
{
throw std::runtime_error("Existing output file "+MBUnicode::PathToUTF8(OutFilePath)+
" has invalid DocGen header, file is not overriden");
}
std::string DocGenEnd = "\n//DocGen_End";
size_t EndPosition = TotalFileData.find(DocGenEnd);
if(EndPosition == TotalFileData.npos)
{
throw std::runtime_error("Existing output file "+MBUnicode::PathToUTF8(OutFilePath)+
" has invalid DocGen header, file is not overriden");
}
EndPosition += DocGenEnd.size();
EndPosition = TotalFileData.find('\n',EndPosition);
if(EndPosition == TotalFileData.npos)
{
throw std::runtime_error("Existing output file "+MBUnicode::PathToUTF8(OutFilePath)+
" has invalid DocGen header, file is not overriden");
}
EndPosition += 1;
PrevBodyContent = TotalFileData.substr(EndPosition);
}
std::ofstream OutFileStream = std::ofstream(OutFilePath,std::ios::out|std::ios::binary);
MBUtility::MBFileOutputStream OutStream(&OutFileStream);
p_WriteClass(Class.second,OutStream);
OutFileStream<<PrevBodyContent;
}
}
}