static bool BuildCodeSnippetsTxt(string SamplesDir, BuildActions Actions) { if ((Actions & BuildActions.Clean) != 0) { APISnippets.CleanAllFiles(); } if ((Actions & BuildActions.Build) == 0) { return true; } //We should be able to trim this down further. DirectoryInfo Dir = new DirectoryInfo(SamplesDir); List<string> Files = new List<string>(); foreach (string FileName in Directory.GetFiles(SamplesDir, "*.cpp", SearchOption.AllDirectories)) { if (!FileName.EndsWith(".generated.cpp")) { Files.Add(FileName); } } foreach (string FileName in Directory.GetFiles(SamplesDir, "*.h", SearchOption.AllDirectories)) { if (!FileName.EndsWith(".generated.h")) { Files.Add(FileName); } } //Do the harvesting work. { const string OpeningTag = "///CODE_SNIPPET_START:"; const string ClosingTag = "///CODE_SNIPPET_END"; const string SeeTag = "@see"; APISnippets Snippets = new APISnippets(); List<string> CurrentSnippetPageNames = new List<string>(4); //Probably won't have a snippet that is shared by more than four different pages. List<string> SeePageNames = new List<string>(4); //More than four of these on one line will probably not happen. string CurrentLine; char[] WhiteSpace = {' ', '\t'}; //Doubles as our token delimiter in one place - noted in comments. string[] ClassMemberDelimiters = new string[2] { "::", "()" }; bool IsSnippetBeingProcessed = false; bool WasPreviousLineBlank = false; //Two blank lines in a row will end a code block. Don't allow this to happen in a single snippet. foreach (string FileName in Files) { // Read the file and display it line by line. System.IO.StreamReader file = new System.IO.StreamReader(FileName); int LineNumber = 0; while ((CurrentLine = file.ReadLine()) != null) { ++LineNumber; CurrentLine = CurrentLine.TrimStart(WhiteSpace); if (!CurrentLine.Contains(OpeningTag)) { continue; } CurrentSnippetPageNames = CurrentLine.Split(WhiteSpace, StringSplitOptions.RemoveEmptyEntries).ToList<string>(); //Whitespace is used to delimit our API/snippet page names here. while ((CurrentSnippetPageNames.Count > 0) && (!CurrentSnippetPageNames[0].Contains(OpeningTag))) { CurrentSnippetPageNames.RemoveAt(0); //Remove everything before the opening tag. } if (CurrentSnippetPageNames.Count > 0) { CurrentSnippetPageNames.RemoveAt(0); //Remove the opening tag, which is always in position 0 by this point. } if (CurrentSnippetPageNames.Count < 1) { Console.WriteLine("Error: OpeningTag for snippet harvesting found without any API pages specified."); return false; } IsSnippetBeingProcessed = true; foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { Snippets.AddSnippet(CurrentSnippetPageName); if (!Snippets.AddSnippetText(CurrentSnippetPageName, "**" + Path.GetFileName(FileName) + "** at line " + LineNumber + ":" + Environment.NewLine + Environment.NewLine)) { Console.WriteLine("Error: Failed to add header text to snippet for " + CurrentSnippetPageName + " from source file " + FileName + " at line " + LineNumber); return false; } } bool FinishSnippetAfterThisLine = false; while ((CurrentLine = file.ReadLine()) != null) { ++LineNumber; string TrimmedLine = CurrentLine.TrimStart(WhiteSpace); //This is actually a C# same-line whitespace check, not our token delimiters. string TrimmedLineLower = TrimmedLine.ToLower(); if (TrimmedLine.Contains(OpeningTag)) { //Snippets do not currently support overlapping. If they did, closing tags should be explicit about which entries are ending, and we'd need to skip lines with opening tags. Console.WriteLine("Error: Nested OpeningTag found in " + FileName + " at line " + LineNumber + "! This is not supported. Snippet harvesting process will fail."); return false; } else if (TrimmedLine.StartsWith(ClosingTag)) { //We're done with this snippet now. Mark that we can end cleanly. IsSnippetBeingProcessed = false; foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { Snippets.FinishCurrentSnippet(CurrentSnippetPageName); } break; } else if (TrimmedLine.Contains(ClosingTag)) { //We will be done this snippet once we add this line (minus the tag). FinishSnippetAfterThisLine = true; CurrentLine = CurrentLine.Replace(ClosingTag, "//"); } for (int TrimmedLineStartingIndex = TrimmedLineLower.IndexOf(SeeTag); TrimmedLineStartingIndex >= 0; TrimmedLineStartingIndex = TrimmedLineLower.Substring(TrimmedLineStartingIndex + SeeTag.Length).Contains(SeeTag) ? (TrimmedLineStartingIndex + SeeTag.Length + TrimmedLineLower.Substring(TrimmedLineStartingIndex + SeeTag.Length).IndexOf(SeeTag)) : -2) { int FirstCharacter = TrimmedLineStartingIndex + SeeTag.Length; SeePageNames = TrimmedLine.Substring(FirstCharacter).Split(WhiteSpace, StringSplitOptions.RemoveEmptyEntries).ToList<string>(); //Whitespace is used to delimit our API/snippet page names here. if (SeePageNames.Count > 0) { string SeeText = SeePageNames[0]; string[] SeeTextBreakdown = SeeText.Split(ClassMemberDelimiters, StringSplitOptions.RemoveEmptyEntries); foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { if (SeeTextBreakdown.Length == 2) { //This is considered "class::member" format. if (!Snippets.AddSeeText(CurrentSnippetPageName, "1. [](API:" + SeeText + ")" + Environment.NewLine)) { Console.WriteLine("Error: Failed to add text to snippet's \"See Also\" portion for " + CurrentSnippetPageName + " from source file " + FileName + " at line " + LineNumber); return false; } } else { if (!Snippets.AddSeeText(CurrentSnippetPageName, "1. [](" + SeeText + ")" + Environment.NewLine)) { Console.WriteLine("Error: Failed to add text to snippet's \"See Also\" portion for " + CurrentSnippetPageName + " from source file " + FileName + " at line " + LineNumber); return false; } } } } } if (CurrentLine.Trim().Length < 1) { if (WasPreviousLineBlank) { //Two (or more) blank lines in a row! Not permitted. continue; } else { WasPreviousLineBlank = true; } } else { WasPreviousLineBlank = false; } //This line should be added to the snippet(s) named in the "CODE_SNIPPET_START" line. Capture it. We need to add our own newline. foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { if (!Snippets.AddSnippetText(CurrentSnippetPageName, '\t' + CurrentLine + Environment.NewLine)) { Console.WriteLine("Error: Failed to add text to snippet for " + CurrentSnippetPageName + " from source file " + FileName + " at line " + LineNumber); return false; } } if (FinishSnippetAfterThisLine) { IsSnippetBeingProcessed = false; foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { Snippets.FinishCurrentSnippet(CurrentSnippetPageName); } break; } } } //If we hit the end of the file while harvesting a snippet, we should fail or have a warning. We could also just ignore the failure to end cleanly and just store the text as-is. //Opting for outright failure at the moment so that these errors don't go unnoticed. if (IsSnippetBeingProcessed) { Console.WriteLine("Code snippet start tag not matched with code snippet end tag in " + FileName); return false; } file.Close(); } if (!Snippets.WriteSnippetsToFiles()) { Console.WriteLine("Error writing intermediate snippet files."); return false; } } //Completed without error. return true; }
static bool BuildCodeSnippetsTxt(string SamplesDir, BuildActions Actions) { if ((Actions & BuildActions.Clean) != 0) { APISnippets.CleanAllFiles(); } if ((Actions & BuildActions.Build) == 0) { return true; } //We should be able to trim this down further. DirectoryInfo Dir = new DirectoryInfo(SamplesDir); List<string> Files = new List<string>(); foreach (string FileName in Directory.GetFiles(SamplesDir, "*.cpp", SearchOption.AllDirectories)) { if (!FileName.EndsWith(".generated.cpp")) { Files.Add(FileName); } } foreach (string FileName in Directory.GetFiles(SamplesDir, "*.h", SearchOption.AllDirectories)) { if (!FileName.EndsWith(".generated.h")) { Files.Add(FileName); } } //Do the harvesting work. { const string OpeningTag = "///CODE_SNIPPET_START:"; const string ClosingTag = "///CODE_SNIPPET_END"; APISnippets Snippets = new APISnippets(); List<string> CurrentSnippetPageNames = new List<string>(4); //Probably won't have a snippet that is shared by more than four different pages. string CurrentLine; char[] WhiteSpace = {' ', '\t'}; //Doubles as our token delimiter in one place - noted in comments. bool IsSnippetBeingProcessed = false; bool WasPreviousLineBlank = false; //Two blank lines in a row will end a code block. Don't allow this to happen in a single snippet. foreach (string FileName in Files) { // Read the file and display it line by line. System.IO.StreamReader file = new System.IO.StreamReader(FileName); while ((CurrentLine = file.ReadLine()) != null) { CurrentLine = CurrentLine.TrimStart(WhiteSpace); if (!CurrentLine.StartsWith(OpeningTag)) { continue; } CurrentSnippetPageNames = CurrentLine.Split(WhiteSpace).ToList<string>(); //Whitespace is used to delimit our API/snippet page names here. CurrentSnippetPageNames.RemoveAt(0); //Remove the opening tag, which is always in position 0 after empties have been cleared out. CurrentSnippetPageNames.RemoveAll(entry => (entry.Length < 1)); //Blank entries can show up in the list. Remove them. if (CurrentSnippetPageNames.Count < 1) { Console.WriteLine("Warning: OpeningTag for snippet harvesting found without any API pages specified."); continue; } IsSnippetBeingProcessed = true; foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { Snippets.AddSnippet(CurrentSnippetPageName); } while ((CurrentLine = file.ReadLine()) != null) { string TrimmedLine = CurrentLine.TrimStart(WhiteSpace); //This is actually a C# same-line whitespace check, not our token delimiters. if (TrimmedLine.StartsWith(OpeningTag)) { //Snippets do not currently support overlapping. If they did, closing tags should be explicit about which entries are ending, and we'd need to skip lines with opening tags. Console.WriteLine("Error: Nested OpeningTag found! This is not supported. Snippet harvesting process will fail."); return false; } else if (TrimmedLine.StartsWith(ClosingTag)) { //We're done with this snippet now. Mark that we can end cleanly. IsSnippetBeingProcessed = false; break; } if (CurrentLine.Trim().Length < 1) { if (WasPreviousLineBlank) { //Two (or more) blank lines in a row! Not permitted. continue; } else { WasPreviousLineBlank = true; } } else { WasPreviousLineBlank = false; } //This line should be added to the snippet(s) named in the "CODE_SNIPPET_START" line. Capture it. We need to add our own newline. foreach (string CurrentSnippetPageName in CurrentSnippetPageNames) { if (!Snippets.AddSnippetText(CurrentSnippetPageName, CurrentLine + Environment.NewLine)) { Console.WriteLine("Error adding text to snippet for " + CurrentSnippetPageName); return false; } } } } //If we hit the end of the file while harvesting a snippet, we should fail or have a warning. We could also just ignore the failure to end cleanly and just store the text as-is. //Opting for outright failure at the moment so that these errors don't go unnoticed. if (IsSnippetBeingProcessed) { Console.WriteLine("Code snippet start tag not matched with code snippet end tag in " + FileName); return false; } file.Close(); } if (!Snippets.WriteSnippetsToFiles()) { Console.WriteLine("Error writing intermediate snippet files."); return false; } } //Completed without error. return true; }