123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386 |
- // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
- #include "UDNParser.h"
- #include "DocumentationStyle.h"
- #include "Fonts/SlateFontInfo.h"
- #include "Misc/Paths.h"
- #include "HAL/FileManager.h"
- #include "Misc/FileHelper.h"
- #include "Framework/Application/SlateApplication.h"
- #include "Widgets/Layout/SSeparator.h"
- #include "Widgets/Images/SImage.h"
- #include "Widgets/Text/STextBlock.h"
- #include "Widgets/Text/SRichTextBlock.h"
- #include "Widgets/Layout/SBox.h"
- #include "Widgets/Input/SButton.h"
- #include "EditorStyleSet.h"
- #include "Editor/EditorPerProjectUserSettings.h"
- #include "Developer/MessageLog/Public/MessageLogModule.h"
- #include "Logging/MessageLog.h"
- //#include "Editor/Documentation/Private/DocumentationLink.h"
- #include "DocumentationLink.h"
- #include "ISourceCodeAccessor.h"
- #include "ISourceCodeAccessModule.h"
- #include "IContentBrowserSingleton.h"
- #include "ContentBrowserModule.h"
- #include "DesktopPlatformModule.h"
- #include "Framework/Notifications/NotificationManager.h"
- #include "Widgets/Notifications/SNotificationList.h"
- #include "Widgets/Input/SHyperlink.h"
- #include "Subsystems/AssetEditorSubsystem.h"
- #define LOCTEXT_NAMESPACE "IntroTutorials"
- FName UDNParseErrorLog("UDNParser");
- #include "ExtDocumentation.h"
- namespace LinkPrefixes
- {
- static const FString DocLinkSpecifier( TEXT( "DOCLINK:" ) );
- static const FString TutorialLinkSpecifier( TEXT( "TUTORIALLINK:" ) );
- static const FString HttpLinkSpecifier( TEXT( "http://" ) );
- static const FString HttpsLinkSpecifier( TEXT( "https://" ) );
- static const FString CodeLinkSpecifier(TEXT("CODELINK:"));
- static const FString AssetLinkSpecifier(TEXT("ASSETLINK:"));
- }
- TSharedRef< FExtUDNParser > FExtUDNParser::Create( const TSharedPtr< FParserConfiguration >& ParserConfig, const FDocumentationStyle& Style )
- {
- TSharedPtr< FParserConfiguration > FinalParserConfig = ParserConfig;
- if ( !FinalParserConfig.IsValid() )
- {
- struct Local
- {
- static void OpenLink( const FString& Link )
- {
- if ( !IDocumentation::Get()->Open( Link, FDocumentationSourceInfo(TEXT("udn_parser")) ) )
- {
- FNotificationInfo Info( NSLOCTEXT("FExtUDNParser", "FailedToOpenLink", "Failed to Open Link") );
- FSlateNotificationManager::Get().AddNotification(Info);
- }
- }
- };
- FinalParserConfig = FParserConfiguration::Create();
- FinalParserConfig->OnNavigate = FOnNavigate::CreateStatic( &Local::OpenLink );
- }
- TSharedRef< FExtUDNParser > Parser = MakeShareable( new FExtUDNParser( FinalParserConfig.ToSharedRef(), Style ) );
- Parser->Initialize();
- return Parser;
- }
- FExtUDNParser::FExtUDNParser( const TSharedRef< FParserConfiguration >& InConfiguration, const FDocumentationStyle& InStyle )
- : Configuration( InConfiguration )
- , Style(InStyle)
- , WrapAt(600.f)
- , ContentWidth(600.f)
- {
- }
- void FExtUDNParser::Initialize()
- {
- FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");
- MessageLogModule.RegisterLogListing( UDNParseErrorLog, LOCTEXT("UDNParser", "UDN Parse Errors") );
- // Set up rules for interpreting strings as tokens
- TokenLibrary.Add(FTokenPair(TEXT("#"), EUDNToken::Pound));
- TokenLibrary.Add(FTokenPair(TEXT("["), EUDNToken::OpenBracket));
- TokenLibrary.Add(FTokenPair(TEXT("]"), EUDNToken::CloseBracket));
- TokenLibrary.Add(FTokenPair(TEXT("("), EUDNToken::OpenParenthesis));
- TokenLibrary.Add(FTokenPair(TEXT(")"), EUDNToken::CloseParenthesis));
- TokenLibrary.Add(FTokenPair(TEXT("1."), EUDNToken::Numbering));
- TokenLibrary.Add(FTokenPair(TEXT("!"), EUDNToken::Bang));
- TokenLibrary.Add(FTokenPair(TEXT("EXCERPT"), EUDNToken::Excerpt));
- TokenLibrary.Add(FTokenPair(TEXT("VAR"), EUDNToken::Variable));
- TokenLibrary.Add(FTokenPair(TEXT(":"), EUDNToken::Colon));
- TokenLibrary.Add(FTokenPair(TEXT("/"), EUDNToken::Slash));
- TokenLibrary.Add(FTokenPair(TEXT("-"), EUDNToken::Dash));
- TokenLibrary.Add(FTokenPair(TEXT("Availability:"), EUDNToken::MetadataAvailability));
- TokenLibrary.Add(FTokenPair(TEXT("Title:"), EUDNToken::MetadataTitle));
- TokenLibrary.Add(FTokenPair(TEXT("Crumbs:"), EUDNToken::MetadataCrumbs));
- TokenLibrary.Add(FTokenPair(TEXT("Description:"), EUDNToken::MetadataDescription));
- TokenLibrary.Add(FTokenPair(TEXT("%"), EUDNToken::Percentage));
- TokenLibrary.Add(FTokenPair(TEXT("*"), EUDNToken::Asterisk));
- // Set up rules for interpreting series of symbols into a line of Slate content
- TArray<EUDNToken::Type> TokenArray;
-
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::Asterisk);
- TokenArray.Add(EUDNToken::Asterisk);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::Asterisk);
- TokenArray.Add(EUDNToken::Asterisk);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::BoldContent));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::Percentage);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::Percentage);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::VariableReference));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::Numbering);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::NumberedContent, true));
-
- TokenArray.Empty();
- for (int32 i = 0; i < 3; ++i)
- {
- TokenArray.Add(EUDNToken::Dash);
- }
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::HorizontalRule));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::Pound);
- TokenArray.Add(EUDNToken::Pound);
- TokenArray.Add(EUDNToken::Pound);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::Header2, true));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::Pound);
- TokenArray.Add(EUDNToken::Pound);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::Header1, true));
-
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- TokenArray.Add(EUDNToken::OpenParenthesis);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseParenthesis);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::Link));
-
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Bang);
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- TokenArray.Add(EUDNToken::OpenParenthesis);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseParenthesis);
- TokenArray.Add(EUDNToken::CloseBracket);
- TokenArray.Add(EUDNToken::OpenParenthesis);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseParenthesis);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::ImageLink));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::Bang);
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- TokenArray.Add(EUDNToken::OpenParenthesis);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseParenthesis);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::Image));
-
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Excerpt);
- TokenArray.Add(EUDNToken::Colon);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::ExcerptOpen));
-
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Slash);
- TokenArray.Add(EUDNToken::Excerpt);
- TokenArray.Add(EUDNToken::Colon);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::ExcerptClose));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::MetadataAvailability);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::MetadataAvailability, true));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::MetadataTitle);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::MetadataTitle, true));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::MetadataCrumbs);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::MetadataCrumbs, true));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::MetadataDescription);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::MetadataDescription, true));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Variable);
- TokenArray.Add(EUDNToken::Colon);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Variable);
- TokenArray.Add(EUDNToken::CloseBracket);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::Variable));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Variable);
- TokenArray.Add(EUDNToken::Colon);
- TokenArray.Add(EUDNToken::Content);
- TokenArray.Add(EUDNToken::CloseBracket);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::VariableOpen));
- TokenArray.Empty();
- TokenArray.Add(EUDNToken::OpenBracket);
- TokenArray.Add(EUDNToken::Slash);
- TokenArray.Add(EUDNToken::Variable);
- TokenArray.Add(EUDNToken::CloseBracket);
- LineLibrary.Add(FTokenConfiguration(TokenArray, FUDNLine::VariableClose));
- }
- FExtUDNParser::~FExtUDNParser()
- {
- if ( FModuleManager::Get().IsModuleLoaded("MessageLog") )
- {
- FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");
- MessageLogModule.UnregisterLogListing(UDNParseErrorLog);
- }
- }
- bool FExtUDNParser::LoadLink( const FString& Link, TArray<FString>& ContentLines )
- {
- FMessageLog UDNParserLog(UDNParseErrorLog);
- const FString SourcePath = FExtDocumentationLink::ToSourcePath( Link );
-
- if (!FPaths::FileExists(SourcePath))
- {
- return false;
- }
- TArray<uint8> Buffer;
- bool bLoadSuccess = FFileHelper::LoadFileToArray(Buffer, *SourcePath);
- if ( bLoadSuccess )
- {
- FString Result;
- FFileHelper::BufferToString( Result, Buffer.GetData(), Buffer.Num() );
- // Now read it
- // init traveling pointer
- TCHAR* Ptr = Result.GetCharArray().GetData();
- // iterate over the lines until complete
- bool bIsDone = false;
- while (!bIsDone)
- {
- // Store the location of the first character of this line
- TCHAR* Start = Ptr;
- // Advance the char pointer until we hit a newline character
- while (*Ptr && *Ptr!='\r' && *Ptr!='\n')
- {
- Ptr++;
- }
- // If this is the end of the file, we're done
- if (*Ptr == 0)
- {
- bIsDone = 1;
- }
- // Handle different line endings. If \r\n then NULL and advance 2, otherwise NULL and advance 1
- // This handles \r, \n, or \r\n
- else if ( *Ptr=='\r' && *(Ptr+1)=='\n' )
- {
- // This was \r\n. Terminate the current line, and advance the pointer forward 2 characters in the stream
- *Ptr++ = 0;
- *Ptr++ = 0;
- }
- else
- {
- // Terminate the current line, and advance the pointer to the next character in the stream
- *Ptr++ = 0;
- }
- ContentLines.Add(Start);
- }
- }
- else
- {
- UDNParserLog.Error(FText::Format(LOCTEXT("LoadingError", "Loading document '{0}' failed."), FText::FromString(SourcePath)));
- }
- if ( !bLoadSuccess && GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- return bLoadSuccess;
- }
- bool FExtUDNParser::Parse(const FString& Link, TArray<FExcerpt>& OutExcerpts, FUDNPageMetadata& OutMetadata)
- {
- FMessageLog UDNParserLog(UDNParseErrorLog);
- TArray<FString> ContentLines;
- if ( LoadLink( Link, ContentLines ) )
- {
- TArray<FExcerpt> TempExcerpts;
- const FString SourcePath = FExtDocumentationLink::ToSourcePath( Link );
- bool bParseSuccess = ParseSymbols( Link, ContentLines, FPaths::GetPath( SourcePath ), TempExcerpts, OutMetadata );
- if (bParseSuccess)
- {
- OutExcerpts = TempExcerpts;
- return true;
- }
- else
- {
- if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- UDNParserLog.Error(FText::Format(LOCTEXT("GeneralParsingError", "Parsing document '{0}' failed."), FText::FromString(SourcePath)));
- }
- }
- return false;
- }
- bool FExtUDNParser::GetExcerptContent( const FString& Link, FExcerpt& Excerpt, bool bInSimpleText/* = false*/ )
- {
- FMessageLog UDNParserLog(UDNParseErrorLog);
- TArray<FString> ContentLines;
- if ( LoadLink( Link, ContentLines ) )
- {
- bool bSimpleTextBackup = bSimpleText;
- bSimpleText = bInSimpleText;
- Excerpt.Content = GenerateExcerptContent( Link, Excerpt, ContentLines, Excerpt.LineNumber);
- bSimpleText = bSimpleTextBackup;
- return true;
- }
- else
- {
- if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- UDNParserLog.Error(FText::Format(LOCTEXT("GeneralExcerptError", "Generating a Widget for document '{0}' Excerpt '{1}' failed."), FText::FromString( FExtDocumentationLink::ToSourcePath( Link ) ), FText::FromString(Excerpt.Name) ));
- }
- return false;
- }
- void FExtUDNParser::SetWrapAt( TAttribute<float> InWrapAt )
- {
- WrapAt = InWrapAt;
- }
- int32 FExtUDNParser::FTokenConfiguration::CalculatedExpectedContentStrings()
- {
- int32 ExpectedContentStrings = 0;
- for (int32 i = 0; i < TokensAccepted.Num(); ++i)
- {
- if (TokensAccepted[i] == EUDNToken::Content) {++ExpectedContentStrings;}
- }
- return ExpectedContentStrings;
- }
- TSharedPtr<FSlateDynamicImageBrush> FExtUDNParser::GetDynamicBrushFromImagePath(FString Filename)
- {
- FName BrushName( *Filename );
- if (FPaths::GetExtension(Filename) == TEXT("png") || FPaths::GetExtension(Filename) == TEXT("jpg"))
- {
- FArchive* ImageArchive = IFileManager::Get().CreateFileReader(*Filename);
- if (ImageArchive && FSlateApplication::IsInitialized())
- {
- if (FSlateRenderer* Renderer = FSlateApplicationBase::Get().GetRenderer())
- {
- TSharedPtr<FSlateDynamicImageBrush> AlreadyExistingImageBrush;
- for (int32 i = 0; i < DynamicBrushesUsed.Num(); ++i)
- {
- if (DynamicBrushesUsed[i]->GetResourceName() == BrushName) { AlreadyExistingImageBrush = DynamicBrushesUsed[i]; break; }
- }
- if (AlreadyExistingImageBrush.IsValid())
- {
- return AlreadyExistingImageBrush;
- }
- else
- {
- FIntPoint Size = Renderer->GenerateDynamicImageResource(BrushName);
- return MakeShareable(new FSlateDynamicImageBrush(BrushName, FVector2D(Size.X, Size.Y)));
- }
- }
- }
- }
- return TSharedPtr<FSlateDynamicImageBrush>();
- }
- FString FExtUDNParser::ConvertSymbolIntoAString(const FUDNToken& Token)
- {
- if (Token.TokenType == EUDNToken::Content)
- {
- return Token.Content;
- }
- for (int32 i = 0; i < TokenLibrary.Num(); ++i)
- {
- auto& LibraryToken = TokenLibrary[i];
- if (LibraryToken.TokenType == Token.TokenType)
- {
- return LibraryToken.ParseText;
- }
- }
- return FString();
- }
- FString FExtUDNParser::ConvertSymbolsIntoAString(const TArray<FUDNToken>& TokenList, int32 StartingAfterIndex)
- {
- bool bIsInVariableSubstitution = false;
- FString Output;
- for (int32 i = StartingAfterIndex; i < TokenList.Num(); ++i)
- {
- auto& Token = TokenList[i];
- if(Token.TokenType == EUDNToken::Percentage)
- {
- bIsInVariableSubstitution = !bIsInVariableSubstitution;
- }
- if(!bIsInVariableSubstitution && Token.TokenType != EUDNToken::Percentage)
- {
- Output += ConvertSymbolIntoAString(Token);
- }
- }
- return Output;
- }
- bool FExtUDNParser::ParseLineIntoSymbols(int32 LineNumber, const FString& Line, TArray<FUDNToken>& SymbolList)
- {
- if (!Line.IsEmpty())
- {
- FString ChoppedLine;
- bool bFoundSymbol = false;
- for (int32 i = 0; i < TokenLibrary.Num(); ++i)
- {
- auto& Symbol = TokenLibrary[i];
- FString TrimmedLine = Line;
- TrimmedLine.TrimStartInline();
- if (TrimmedLine.StartsWith(Symbol.ParseText))
- {
- ChoppedLine = TrimmedLine.RightChop(Symbol.ParseText.Len());
- SymbolList.Add(FUDNToken(Symbol.TokenType));
- bFoundSymbol = true;
- break;
- }
- }
-
- if (!bFoundSymbol)
- {
- struct Local
- {
- static bool CharIsValid(const TCHAR& Char)
- {
- return
- Char != LITERAL(TCHAR, '[') &&
- Char != LITERAL(TCHAR, ']') &&
- Char != LITERAL(TCHAR, '(') &&
- Char != LITERAL(TCHAR, ')') &&
- Char != LITERAL(TCHAR, '%') &&
- Char != LITERAL(TCHAR, '*');
- }
- static bool FirstCharIsValid(const TCHAR& Char)
- {
- return
- Char != LITERAL(TCHAR, '[') &&
- Char != LITERAL(TCHAR, ']') &&
- Char != LITERAL(TCHAR, '(') &&
- Char != LITERAL(TCHAR, ')') &&
- Char != LITERAL(TCHAR, '!') &&
- Char != LITERAL(TCHAR, ':') &&
- Char != LITERAL(TCHAR, '/') &&
- Char != LITERAL(TCHAR, '%') &&
- Char != LITERAL(TCHAR, '*');
- }
- };
- int32 CharIdx = 0;
- for (; CharIdx < Line.Len(); ++CharIdx)
- {
- auto Char = Line[CharIdx];
- bool bIsContentChar = CharIdx == 0 ? Local::FirstCharIsValid(Char) : Local::CharIsValid(Char);
- if ( !bIsContentChar && CharIdx != 0 )
- {
- FString LeftString = Line.Left(CharIdx);
- ChoppedLine = Line.RightChop(CharIdx);
- SymbolList.Add(FUDNToken(EUDNToken::Content, LeftString));
- bFoundSymbol = true;
- break;
- }
- }
- // Indicates that we went to the end of the line, so the entire thing is a symbol
- if (CharIdx == Line.Len())
- {
- ChoppedLine = FString();
- SymbolList.Add(FUDNToken(EUDNToken::Content, Line));
- bFoundSymbol = true;
- }
- }
- if (!bFoundSymbol)
- {
- // Indicates that we found an unknown token, error
- FMessageLog UDNParserLog(UDNParseErrorLog);
- UDNParserLog.Error(FText::Format(LOCTEXT("TokenParseError", "Line {0}: Token '{1}' could not be parsed properly."), FText::AsNumber(LineNumber), FText::FromString(Line)));
- if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- return false;
- }
- else
- {
- return ParseLineIntoSymbols(LineNumber, ChoppedLine, SymbolList);
- }
- }
- // Line is out of characters
- return true;
- }
- FUDNLine FExtUDNParser::ParseLineIntoUDNContent(int32 LineNumber, const FString& Line)
- {
- FMessageLog UDNParserLog(UDNParseErrorLog);
- FString TrimmedLine = Line;
- TrimmedLine.TrimStartInline();
- FUDNLine OutputLine;
- TArray<FUDNToken> SymbolList;
- bool bSuccessful = ParseLineIntoSymbols(LineNumber, TrimmedLine, SymbolList);
- if (bSuccessful)
- {
- if (SymbolList.Num() > 0)
- {
- bool bLineWasMatched = false;
- for (int32 i = 0; i < LineLibrary.Num() && !bLineWasMatched; ++i)
- {
- auto& LineConfig = LineLibrary[i];
- TArray<FString> Contents;
- FString CurrentContentString;
- bool bMatch = true;
- bool bInVariableSubstitution = false;
- int32 SymbolIdx = 0;
- for (int32 TokenIdx = 0; bMatch && TokenIdx < LineConfig.TokensAccepted.Num(); ++TokenIdx)
- {
- EUDNToken::Type& Token = LineConfig.TokensAccepted[TokenIdx];
- if (SymbolIdx < SymbolList.Num())
- {
- FUDNToken& Symbol = SymbolList[SymbolIdx];
- if(bInVariableSubstitution && Symbol.TokenType != EUDNToken::Percentage)
- {
- ++SymbolIdx;
- }
- else if (Symbol.TokenType == EUDNToken::Percentage)
- {
- bInVariableSubstitution = !bInVariableSubstitution;
- ++SymbolIdx;
- }
- else
- {
- if (Token == EUDNToken::Content)
- {
- check(TokenIdx + 1 < LineConfig.TokensAccepted.Num() && LineConfig.TokensAccepted[TokenIdx+1] != EUDNToken::Content);
- EUDNToken::Type& NextToken = LineConfig.TokensAccepted[TokenIdx+1];
- if (Symbol.TokenType == NextToken)
- {
- Contents.Add(CurrentContentString);
- CurrentContentString.Empty();
- }
- else
- {
- CurrentContentString += ConvertSymbolIntoAString(Symbol);
- ++SymbolIdx;
- --TokenIdx;
- }
- }
- else
- {
- if (Symbol.TokenType != Token)
- {
- bMatch = false;
- }
- ++SymbolIdx;
- }
- }
- }
- else
- {
- if(bInVariableSubstitution)
- {
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableSubstitutionError", "Line {0}: Line '{1}' variable substitution was not terminated"), FText::AsNumber(LineNumber), FText::FromString(Line)));
- }
- if (Token != EUDNToken::Content)
- {
- bMatch = false;
- }
- }
- }
- if (bMatch && (SymbolIdx == SymbolList.Num() || LineConfig.bAcceptTrailingSymbolDumpAsContent))
- {
- if (LineConfig.CalculatedExpectedContentStrings() == Contents.Num())
- {
- OutputLine.ContentType = LineConfig.OutputLineType;
- for (const FString Content : Contents)
- {
- OutputLine.AdditionalContent.Add(Content);
- }
- if (LineConfig.bAcceptTrailingSymbolDumpAsContent)
- {
- OutputLine.AdditionalContent.Add(ConvertSymbolsIntoAString(SymbolList, SymbolIdx).TrimStart());
- }
- }
- else
- {
- if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- UDNParserLog.Error(FText::Format(LOCTEXT("LineConvertError", "Line {0}: Line '{1}' could not converted into a Slate widget."), FText::AsNumber(LineNumber), FText::FromString(Line)));
- }
- check(!bLineWasMatched);
- bLineWasMatched = true;
- }
- }
- if (!bLineWasMatched)
- {
- OutputLine.ContentType = FUDNLine::Content;
- OutputLine.AdditionalContent.Add(ConvertSymbolsIntoAString(SymbolList));
- }
- }
- else
- {
- // empty line
- OutputLine.ContentType = FUDNLine::Whitespace;
- }
- }
- else
- {
- if ( GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- UDNParserLog.Error(FText::Format(LOCTEXT("LineParseError", "Line {0}: Line '{1}' could not be parsed into symbols properly."), FText::AsNumber(LineNumber), FText::FromString(Line)));
- }
- return OutputLine;
- }
- void FExtUDNParser::AppendExcerpt(TSharedPtr<SVerticalBox> Box, TSharedRef<SWidget> Content)
- {
- Box->AddSlot()
- .AutoHeight()
- .HAlign(HAlign_Center)
- [
- SNew(SBox)
- .HAlign(HAlign_Left)
- .WidthOverride(ContentWidth)
- .Padding(FMargin(0,0,0,8.f))
- [
- SNew(SHorizontalBox)
- +SHorizontalBox::Slot()
- .AutoWidth()
- [
- Content
- ]
- ]
- ];
- }
- static void AddLineSeperator(FExcerpt& Excerpt)
- {
- if(!Excerpt.RichText.IsEmpty())
- {
- Excerpt.RichText += LINE_TERMINATOR;
- Excerpt.RichText += LINE_TERMINATOR;
- }
- }
- void FExtUDNParser::AddContentToExcerpt(TSharedPtr<SVerticalBox> Box, const FString& ContentSource, FExcerpt& Excerpt)
- {
- if ( !ContentSource.IsEmpty() )
- {
- const ISlateStyle& DocumentationStyle = FExtDocumentationStyle::Get();
- AppendExcerpt(Box,
- SNew(STextBlock)
- .Text(FText::FromString(ContentSource))
- .TextStyle(DocumentationStyle, Style.ContentStyleName)
- .WrapTextAt(WrapAt)
- );
- AddLineSeperator(Excerpt);
- if (bSimpleText)
- {
- Excerpt.RichText += FString::Printf(TEXT("%s"), *ContentSource);
- }
- else
- {
- Excerpt.RichText += FString::Printf(TEXT("<TextStyle Style=\"%s\">%s</>"), *Style.ContentStyleName.ToString(), *ContentSource);
- }
- }
- }
- TSharedRef< SWidget > FExtUDNParser::GenerateExcerptContent( const FString& InLink, FExcerpt& Excerpt, const TArray<FString>& ContentLines, int32 StartingLineIndex )
- {
- FMessageLog UDNParserLog(UDNParseErrorLog);
- const FString SourcePath = FExtDocumentationLink::ToSourcePath( InLink );
- const FString FullPath = FPaths::GetPath( SourcePath );
- FSlateFontInfo Header1Font = FSlateFontInfo( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 18 );
- FSlateFontInfo Header2Font = FSlateFontInfo( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 14 );
- bool bCriticalError = false;
- FString VariableName;
- FString CurrentStringContent;
- int32 CurrentNumbering = 1;
- TSharedPtr<SVerticalBox> Box;
- TArray<FString> ExcerptStack;
- const ISlateStyle& DocumentationStyle = FExtDocumentationStyle::Get();
- for (int32 CurrentLineNumber = StartingLineIndex; CurrentLineNumber < ContentLines.Num(); ++CurrentLineNumber)
- {
- const FString& CurrentLine = ContentLines[ CurrentLineNumber ];
- const FUDNLine& Line = ParseLineIntoUDNContent(CurrentLineNumber, CurrentLine);
- if (Line.ContentType == FUDNLine::ExcerptOpen)
- {
- ExcerptStack.Push(Line.AdditionalContent[0]);
- Box = SNew( SVerticalBox );
- }
- else if (Line.ContentType == FUDNLine::ExcerptClose)
- {
- if (ExcerptStack.Num() == 0 || Line.AdditionalContent[0] != ExcerptStack.Top())
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("ExcerptCloseError", "Line {0}: Excerpt {1} improperly closed."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- FString ExcerptName = ExcerptStack.Pop();
- if ( ExcerptStack.Num() == 0 )
- {
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- break;
- }
- }
- else if ( Line.ContentType == FUDNLine::VariableOpen )
- {
- if ( !VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableOpenError", "Line {0}: Excerpt {1} improperly attempting to define a variable within a variable."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- VariableName = Line.AdditionalContent[0];
- if ( VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableWithOutName", "Line {0}: Excerpt {1} improperly attempted to define a variable with no name."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- }
- else if ( Line.ContentType == FUDNLine::VariableClose )
- {
- if ( VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableCloseError", "Line {0}: Excerpt {1} improperly attempting to close a variable tag it never opened."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- VariableName.Empty();
- }
- else if ( Line.ContentType == FUDNLine::Variable )
- {
- if ( Line.AdditionalContent.Num() != 2 )
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("Variable", "Line {0}: Excerpt {1} improperly attempted to define a variable with no name."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- VariableName = Line.AdditionalContent[0];
- if ( VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableWithOutName", "Line {0}: Excerpt {1} improperly attempted to define a variable with no name."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- }
- FString ConcatenatedPath;
- TSharedPtr<FSlateDynamicImageBrush> DynamicBrush;
- if (Line.ContentType == FUDNLine::Content && !CurrentStringContent.IsEmpty())
- {
- CurrentStringContent += LINE_TERMINATOR;
- }
- // only emit widgets if we are not inside a variable declaration
- if ( VariableName.IsEmpty() )
- {
- switch (Line.ContentType)
- {
- case FUDNLine::Whitespace:
- // Will only apply whitespace for the first empty line
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- break;
- case FUDNLine::Content:
- CurrentStringContent += Line.AdditionalContent[0];
- break;
- case FUDNLine::BoldContent:
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- AppendExcerpt(Box,
- SNew(STextBlock)
- .Text(FText::FromString(Line.AdditionalContent[0]))
- .TextStyle(DocumentationStyle, Style.BoldContentStyleName)
- );
- AddLineSeperator(Excerpt);
- if (bSimpleText)
- Excerpt.RichText += FString::Printf(TEXT("%s"), *Line.AdditionalContent[0]);
- else
- Excerpt.RichText += FString::Printf(TEXT("<TextStyle Style=\"%s\">%s</>"), *Style.BoldContentStyleName.ToString(), *Line.AdditionalContent[0]);
- break;
- case FUDNLine::NumberedContent:
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent = FString::Printf(TEXT("%i. %s"), CurrentNumbering, *Line.AdditionalContent[0]);
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- ++CurrentNumbering;
- break;
- case FUDNLine::HorizontalRule:
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- Box->AddSlot()
- .HAlign(HAlign_Center)
- [
- SNew(SBox)
- .WidthOverride(ContentWidth)
- .Padding(FMargin(0,0,0,10))
- [
- SNew(SSeparator)
- .SeparatorImage(FExtDocumentationStyle::Get().GetBrush(Style.SeparatorStyleName))
- ]
- ];
- AddLineSeperator(Excerpt);
- break;
- case FUDNLine::Header1:
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- AppendExcerpt(Box,
- SNew(STextBlock)
- .Text(FText::FromString(Line.AdditionalContent[0]))
- .TextStyle(DocumentationStyle, Style.Header1StyleName)
- );
- AddLineSeperator(Excerpt);
- if (bSimpleText)
- Excerpt.RichText += FString::Printf(TEXT("%s"), *Line.AdditionalContent[0]);
- else
- Excerpt.RichText += FString::Printf(TEXT("<TextStyle Style=\"%s\">%s</>"), *Style.Header1StyleName.ToString(), *Line.AdditionalContent[0]);
- break;
- case FUDNLine::Header2:
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- AppendExcerpt(Box,
- SNew(STextBlock)
- .Text(FText::FromString(Line.AdditionalContent[0]))
- .TextStyle(DocumentationStyle, Style.Header2StyleName)
- );
- AddLineSeperator(Excerpt);
- if (bSimpleText)
- Excerpt.RichText += FString::Printf(TEXT("%s"), *Line.AdditionalContent[0]);
- else
- Excerpt.RichText += FString::Printf(TEXT("<TextStyle Style=\"%s\">%s</>"), *Style.Header2StyleName.ToString(), *Line.AdditionalContent[0]);
- break;
- case FUDNLine::Link:
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- AppendExcerpt(Box,
- SNew(SHyperlink)
- .Text(FText::FromString(Line.AdditionalContent[0]))
- .TextStyle(DocumentationStyle, Style.HyperlinkTextStyleName)
- .UnderlineStyle(DocumentationStyle, Style.HyperlinkButtonStyleName)
- .OnNavigate( this, &FExtUDNParser::HandleHyperlinkNavigate, Line.AdditionalContent[1])
- );
- AddLineSeperator(Excerpt);
- if(Line.AdditionalContent[1].Contains(LinkPrefixes::DocLinkSpecifier))
- {
- const FString Link = Line.AdditionalContent[1].RightChop(LinkPrefixes::DocLinkSpecifier.Len());
- Excerpt.RichText += FString::Printf(TEXT("<a id=\"udn\" href=\"%s\" style=\"%s\">%s</>"), *Link, *Style.HyperlinkStyleName.ToString(), *Line.AdditionalContent[0]);
- }
- else if(Line.AdditionalContent[1].Contains(LinkPrefixes::AssetLinkSpecifier))
- {
- const FString Link = Line.AdditionalContent[1].RightChop(LinkPrefixes::AssetLinkSpecifier.Len());
- Excerpt.RichText += FString::Printf(TEXT("<a id=\"asset\" href=\"%s\" style=\"%s\">%s</>"), *Link, *Style.HyperlinkStyleName.ToString(), *Line.AdditionalContent[0]);
- }
- else if(Line.AdditionalContent[1].Contains(LinkPrefixes::CodeLinkSpecifier))
- {
- const FString Link = Line.AdditionalContent[1].RightChop(LinkPrefixes::CodeLinkSpecifier.Len());
- Excerpt.RichText += FString::Printf(TEXT("<a id=\"code\" href=\"%s\" style=\"%s\">%s</>"), *Link, *Style.HyperlinkStyleName.ToString(), *Line.AdditionalContent[0]);
- }
- else if(Line.AdditionalContent[1].Contains(LinkPrefixes::TutorialLinkSpecifier))
- {
- const FString Link = Line.AdditionalContent[1].RightChop(LinkPrefixes::TutorialLinkSpecifier.Len());
- Excerpt.RichText += FString::Printf(TEXT("<a id=\"tutorial\" href=\"%s\" style=\"%s\">%s</>"), *Link, *Style.HyperlinkStyleName.ToString(), *Line.AdditionalContent[0]);
- }
- else
- {
- Excerpt.RichText += FString::Printf(TEXT("<a id=\"browser\" href=\"%s\" style=\"%s\">%s</>"), *InLink, *Style.HyperlinkStyleName.ToString(), *Line.AdditionalContent[0]);
- }
- break;
- case FUDNLine::Image:
- ConcatenatedPath = FullPath / TEXT("Images") / Line.AdditionalContent[1];
- DynamicBrush = GetDynamicBrushFromImagePath(ConcatenatedPath);
- if (DynamicBrush.IsValid())
- {
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- AppendExcerpt(Box,
- SNew( SImage )
- .Image(DynamicBrush.Get())
- .ToolTipText(FText::FromString(Line.AdditionalContent[0]))
- );
- DynamicBrushesUsed.AddUnique(DynamicBrush);
- }
- AddLineSeperator(Excerpt);
- Excerpt.RichText += FString::Printf(TEXT("<img src=\"%s\"></>"), *ConcatenatedPath);
- break;
- case FUDNLine::ImageLink:
- ConcatenatedPath = FullPath / TEXT("Images") / Line.AdditionalContent[1];
- DynamicBrush = GetDynamicBrushFromImagePath(ConcatenatedPath);
- if (DynamicBrush.IsValid())
- {
- AddContentToExcerpt(Box, CurrentStringContent, Excerpt);
- CurrentStringContent.Empty();
- AppendExcerpt(Box,
- SNew(SButton)
- .ContentPadding(0)
- .ButtonStyle(FAppStyle::Get(), "HoverHintOnly" )
- .OnClicked( FOnClicked::CreateSP( this, &FExtUDNParser::OnImageLinkClicked, Line.AdditionalContent[2] ) )
- [
- SNew( SImage )
- .Image(DynamicBrush.Get())
- .ToolTipText(FText::FromString(Line.AdditionalContent[0]))
- ]
- );
- DynamicBrushesUsed.AddUnique(DynamicBrush);
- }
- AddLineSeperator(Excerpt);
- Excerpt.RichText += FString::Printf(TEXT("<img src=\"%s\" href=\"%s\"></>"), *ConcatenatedPath, *Line.AdditionalContent[2]);
- break;
- default: break;
- }
- }
- }
- if ( ExcerptStack.Num() > 0 )
- {
- if ( !bCriticalError )
- {
- UDNParserLog.NewPage( FText::FromString( InLink + TEXT(" [") + Excerpt.Name + TEXT("]") ) );
- }
- for (int32 i = 0; i < ExcerptStack.Num(); ++i)
- {
- UDNParserLog.Error(FText::Format(LOCTEXT("ExcerptMismatchError", "Excerpt {0} was never closed."), FText::FromString(ExcerptStack.Top())));
- }
- bCriticalError = true;
- }
- if ( bCriticalError && GetDefault<UEditorPerProjectUserSettings>()->bDisplayDocumentationLink )
- {
- UDNParserLog.Open();
- }
- if ( bCriticalError )
- {
- return SNew( STextBlock ).Text( LOCTEXT("ExcerptContentLoadingError", "Excerpt {0} could not be loaded. :(") );
- }
- return Box.ToSharedRef();
- }
- bool FExtUDNParser::ParseSymbols(const FString& Link, const TArray<FString>& ContentLines, const FString& FullPath, TArray<FExcerpt>& OutExcerpts, FUDNPageMetadata& OutMetadata)
- {
- FMessageLog UDNParserLog(UDNParseErrorLog);
-
- bool bCriticalError = false;
- FString CurrentStringContent;
- TArray<FString> ExcerptStack;
- int32 ExcerptStartingLineNumber = 0;
- FString VariableName;
- FString VariableValue;
- TMap< FString, FString > Variables;
- for (int32 CurrentLineNumber = 0; CurrentLineNumber < ContentLines.Num(); ++CurrentLineNumber)
- {
- const FString& CurrentLine = ContentLines[ CurrentLineNumber ];
- const FUDNLine& Line = ParseLineIntoUDNContent(CurrentLineNumber, CurrentLine);
-
- bool bIsReadingContent = ExcerptStack.Num() > 0;
-
- if (Line.ContentType == FUDNLine::ExcerptOpen)
- {
- if ( ExcerptStack.Num() == 0 )
- {
- ExcerptStartingLineNumber = CurrentLineNumber;
- }
- ExcerptStack.Push(Line.AdditionalContent[0]);
- }
- else if (Line.ContentType == FUDNLine::ExcerptClose)
- {
- if (ExcerptStack.Num() == 0 || Line.AdditionalContent[0] != ExcerptStack.Top())
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("ExcerptCloseError", "Line {0}: Excerpt {1} improperly closed."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- FString ExcerptName = ExcerptStack.Pop();
-
- if (ExcerptStack.Num() == 0)
- {
- OutExcerpts.Add(FExcerpt(ExcerptName, NULL, Variables, ExcerptStartingLineNumber));
- OutMetadata.ExcerptNames.Add( ExcerptName );
- Variables.Empty();
- ExcerptStartingLineNumber = 0;
- }
- }
- else if ( Line.ContentType == FUDNLine::VariableOpen )
- {
- if ( !VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableOpenError", "Line {0}: Excerpt {1} improperly attempting to define a variable within a variable."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- VariableName = Line.AdditionalContent[0];
- if ( VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableWithOutName", "Line {0}: Excerpt {1} improperly attempted to define a variable with no name."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- }
- else if ( Line.ContentType == FUDNLine::VariableClose )
- {
- if ( VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableCloseError", "Line {0}: Excerpt {1} improperly attempting to close a variable tag it never opened."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- Variables.Add( VariableName, VariableValue );
- VariableName.Empty();
- VariableValue.Empty();
- }
- else if ( Line.ContentType == FUDNLine::Variable )
- {
- if ( Line.AdditionalContent.Num() != 2 )
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("Variable", "Line {0}: Excerpt {1} improperly attempted to define a variable with no name."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- VariableName = Line.AdditionalContent[0];
- VariableValue = Line.AdditionalContent[1];
- if ( VariableName.IsEmpty() )
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- UDNParserLog.Error(FText::Format(LOCTEXT("VariableWithOutName", "Line {0}: Excerpt {1} improperly attempted to define a variable with no name."), FText::AsNumber(CurrentLineNumber), FText::FromString(Line.AdditionalContent[0])));
- bCriticalError = true;
- break;
- }
- Variables.Add( VariableName, VariableValue );
- VariableName.Empty();
- VariableValue.Empty();
- }
- if (!bIsReadingContent)
- {
- switch (Line.ContentType)
- {
- case FUDNLine::MetadataAvailability: OutMetadata.Availability = Line.AdditionalContent[0]; break;
- case FUDNLine::MetadataTitle: OutMetadata.Title = FText::FromString(Line.AdditionalContent[0]); break;
- case FUDNLine::MetadataCrumbs: OutMetadata.Crumbs = FText::FromString(Line.AdditionalContent[0]); break;
- case FUDNLine::MetadataDescription: OutMetadata.Description = FText::FromString(Line.AdditionalContent[0]); break;
- }
- }
- else
- {
- switch (Line.ContentType)
- {
- case FUDNLine::Content:
- case FUDNLine::NumberedContent:
- case FUDNLine::Header1:
- case FUDNLine::Header2:
- case FUDNLine::Image:
- case FUDNLine::Link:
- case FUDNLine::ImageLink:
- {
- if ( !VariableName.IsEmpty() )
- {
- VariableValue += Line.AdditionalContent[0];
- }
- }
- break;
- }
- }
- }
- if ( ExcerptStack.Num() > 0 )
- {
- if ( !bCriticalError )
- {
- UDNParserLog.NewPage( FText::FromString( Link ) );
- }
- for (int32 i = 0; i < ExcerptStack.Num(); ++i)
- {
- UDNParserLog.Error(FText::Format(LOCTEXT("ExcerptMismatchError", "Excerpt {0} was never closed."), FText::FromString(ExcerptStack.Top())));
- }
- bCriticalError = true;
- }
- return !bCriticalError;
- }
- FReply FExtUDNParser::OnImageLinkClicked( FString AdditionalContent )
- {
- NavigateToLink( AdditionalContent );
- return FReply::Handled();
- }
- void FExtUDNParser::HandleHyperlinkNavigate( FString AdditionalContent )
- {
- NavigateToLink( AdditionalContent );
- }
- void FExtUDNParser::NavigateToLink( FString AdditionalContent )
- {
- static const FString DocLinkSpecifier( TEXT( "DOCLINK:" ) );
- static const FString TutorialLinkSpecifier( TEXT( "TUTORIALLINK:" ) );
- static const FString HttpLinkSPecifier( TEXT( "http://" ) );
- static const FString HttpsLinkSPecifier( TEXT( "https://" ) );
- static const FString CodeLinkSpecifier(TEXT("CODELINK:"));
- static const FString AssetLinkSpecifier(TEXT("ASSETLINK:"));
- if (AdditionalContent.StartsWith(DocLinkSpecifier))
- {
- // external link to documentation
- FString DocLink = AdditionalContent.RightChop(DocLinkSpecifier.Len());
- IDocumentation::Get()->Open(DocLink, FDocumentationSourceInfo(TEXT("udn_parser")));
- }
- else if ( AdditionalContent.StartsWith( TutorialLinkSpecifier ) )
- {
- // internal link
- FString InternalLink = AdditionalContent.RightChop( TutorialLinkSpecifier.Len() );
- Configuration->OnNavigate.ExecuteIfBound( InternalLink );
- }
- else if ( AdditionalContent.StartsWith( HttpLinkSPecifier ) || AdditionalContent.StartsWith( HttpsLinkSPecifier ) )
- {
- // external link
- FPlatformProcess::LaunchURL( *AdditionalContent, nullptr, nullptr);
- }
- else if (AdditionalContent.StartsWith(CodeLinkSpecifier))
- {
- FString InternalLink = AdditionalContent.RightChop(CodeLinkSpecifier.Len());
- ParseCodeLink(InternalLink);
- }
- else if (AdditionalContent.StartsWith(AssetLinkSpecifier))
- {
- FString InternalLink = AdditionalContent.RightChop(AssetLinkSpecifier.Len());
- ParseAssetLink(InternalLink);
- }
- else
- {
- // internal link
- Configuration->OnNavigate.ExecuteIfBound( AdditionalContent );
- }
- }
- bool FExtUDNParser::ParseCodeLink(FString &InternalLink)
- {
- // Tokens used by the code parsing. Details in the parse section
- static const FString ProjectSpecifier(TEXT("[PROJECT]"));
- static const FString ProjectRoot(TEXT("[PROJECT]/Source/[PROJECT]/"));
- static const FString ProjectSuffix(TEXT(".uproject"));
- bool bLinkParsedOK = false;
- FString Path;
- int32 Line = 0;
- int32 Col = 0;
- TArray<FString> Tokens;
- InternalLink.ParseIntoArray(Tokens, TEXT(","), 0);
- int32 TokenStringsCount = Tokens.Num();
- if (TokenStringsCount > 0)
- {
- Path = Tokens[0];
- }
- if (TokenStringsCount > 1)
- {
- TTypeFromString<int32>::FromString(Line, *Tokens[1]);
- }
- if (TokenStringsCount > 2)
- {
- TTypeFromString<int32>::FromString(Col, *Tokens[2]);
- }
- ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>("SourceCodeAccess");
- ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor();
- // If we specified generic project specified as the project name try to replace the name with the name of this project
- if (InternalLink.Contains(ProjectSpecifier) == true)
- {
- FString ProjectName = TEXT("Marble");
- // Try to extract the name of the project
- FString ProjectPath = FPaths::GetProjectFilePath();
- if (ProjectPath.EndsWith(ProjectSuffix))
- {
- int32 ProjectPathEndIndex;
- if (ProjectPath.FindLastChar(TEXT('/'), ProjectPathEndIndex) == true)
- {
- ProjectName = ProjectPath.RightChop(ProjectPathEndIndex + 1);
- ProjectName.RemoveFromEnd(*ProjectSuffix);
- }
- }
- // Replace the root path with the name of this project
- FString RebuiltPath = ProjectRoot + Path;
- RebuiltPath.ReplaceInline(*ProjectSpecifier, *ProjectName);
- Path = RebuiltPath;
- }
- // Finally create the complete path - project name and all
- return SourceCodeAccessor.OpenFileAtLine(FPaths::RootDir() + Path, Line, Col);
- }
- bool FExtUDNParser::ParseAssetLink(FString &InternalLink)
- {
- TArray<FString> Token;
- InternalLink.ParseIntoArray(Token, TEXT(","), 0);
-
- if (Token.Num() >= 2)
- {
- FString Action = Token[0];
- FString AssetName = Token[1];
- UObject* RequiredObject = FindFirstObject<UObject>(*AssetName, EFindFirstObjectOptions::NativeFirst | EFindFirstObjectOptions::EnsureIfAmbiguous);
- if (RequiredObject != nullptr)
- {
- if (Action == TEXT("EDIT"))
- {
- GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(RequiredObject);
- }
- else
- {
- FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
- TArray<UObject*> AssetToBrowse;
- AssetToBrowse.Add(RequiredObject);
- ContentBrowserModule.Get().SyncBrowserToAssets(AssetToBrowse);
- }
- }
- }
-
- return false;
- }
- #undef LOCTEXT_NAMESPACE
- #ifdef EXT_DOC_NAMESPACE
- #undef EXT_DOC_NAMESPACE
- #endif
|