3 name:
"Doxygen Filler",
4 description:
"Create/Format Doxygen documentation skeleton for configured methods (default public methods only)",
5 shortcut:
"Ctrl+Alt+Shift+D",
6 wbModules: {
"ScriptEditor" },
7 awesomeFontCode: 0xF518)]
10class SCR_DoxygenFillerPlugin : WorkbenchPlugin
16 [
Attribute(
"1",
category:
"Doxygen",
desc:
"Add Doxygen skeleton to public methods (neither protected nor private)")]
17 protected bool m_bDoxygenPublicMethods;
20 protected bool m_bDoxygenProtectedMethods;
23 protected bool m_bDoxygenPrivateMethods;
26 protected bool m_bDoxygenOverriddenMethods;
29 protected bool m_bDoxygenStaticMethods;
32 protected bool m_bDoxygenObsoleteMethods;
34 [
Attribute(
"MethodPrefix",
category:
"Doxygen",
desc:
"Only document params and return value (without description) for methods starting with these prefixes (e.g Set*, Get*, On*, Is* etc, case-sensitive)")]
35 protected ref array<string> m_aPartialDoxygenPrefixes;
41 [
Attribute(
"1",
category:
"Other",
desc:
"Replace Doxygen blocks with multiple one line comments (" + DOXYGEN_LINE_START +
")")]
42 protected bool m_bConvertDoxygenFormatting;
45 protected bool m_bAddMissingSeparators;
47 [
Attribute(
"1",
category:
"Other",
desc:
"Add \"" + CONSTRUCTOR_COMMENT +
"\" comment to constructor")]
48 protected bool m_bAddConstructorNormalComment;
51 protected bool m_bAddDestructorNormalComment;
57 [
Attribute(defvalue:
"0",
category:
"Debug",
desc:
"Output method parsing errors in the log console")]
58 protected bool m_bOutputMethodParsingErrors;
60 protected static const string METHOD_SEPARATOR =
SCR_StringHelper.DOUBLE_SLASH +
"------------------------------------------------------------------------------------------------";
61 protected static const string DOXYGEN_LINE_START =
SCR_StringHelper.DOUBLE_SLASH +
"!";
62 protected static const string GENERATED_SCRIPT_WARNING =
"Do not modify, this script is generated";
63 protected static const string CONSTRUCTOR_COMMENT =
SCR_StringHelper.DOUBLE_SLASH +
" constructor";
64 protected static const string DESTRUCTOR_COMMENT =
SCR_StringHelper.DOUBLE_SLASH +
" destructor";
65 protected static const string COMMENT_BLOCK_START =
"/" +
"*";
66 protected static const string COMMENT_BLOCK_END =
"*" +
"/";
67 protected static const ref array<string> DOXYGEN_BLOCK_STARTS = { COMMENT_BLOCK_START +
"*", COMMENT_BLOCK_START +
"!" };
68 protected static const string BASE_PARAM =
"\\param";
69 protected static const string FAULTY_PARAM =
"\\param ";
72 protected override void Run()
78 protected bool ProcessAddon()
86 if (m_aPartialDoxygenPrefixes.IsEmpty())
87 m_aPartialDoxygenPrefixes = {
"Get",
"Set",
"On",
"Is" };
89 SCR_ArrayHelperT<string>.RemoveDuplicates(m_aPartialDoxygenPrefixes);
95 protected bool ProcessCurrentFile()
97 ScriptEditor scriptEditor = Workbench.GetModule(ScriptEditor);
102 if (!scriptEditor.GetCurrentFile(filePath))
108 if (filePath == __FILE__)
110 Print(
"Cannot add Doxygen documentation to the Doxygen Filler plugin itself - " + filePath,
LogLevel.NORMAL);
114 string absoluteFilePath;
115 if (!Workbench.GetAbsolutePath(filePath, absoluteFilePath,
true))
117 Print(
"Could not get absolute file path - " + filePath,
LogLevel.WARNING);
123 bool result = ProcessFile(absoluteFilePath);
125 Print(
"Successfully processed " + absoluteFilePath,
LogLevel.NORMAL);
134 protected bool ProcessFile(
string filePath)
137 if (lines.Contains(GENERATED_SCRIPT_WARNING))
139 Print(
"This file is generated - skipping - " + filePath,
LogLevel.WARNING);
143 array<ref Tuple2<int, string>> commentsToAdd = {};
144 array<ref Tuple2<int, string>> commentsToReplace = {};
148 string currentDoxygenComment;
149 bool isInDoxygenCommentBlock;
150 bool isInCommentBlock;
152 int commentBlockIndex;
153 string currentClassName;
156 foreach (
int lineId,
string line : lines)
160 if (resetValues != 2)
161 hasSeparator =
false;
163 currentDoxygenComment =
string.Empty;
164 if (resetValues != 1)
170 commentBlockIndex = 0;
173 if (!isInCommentBlock)
175 commentBlockIndex = line.IndexOfFrom(commentBlockIndex, COMMENT_BLOCK_START);
176 if (commentBlockIndex < 0)
179 isInCommentBlock =
true;
183 commentBlockIndex = line.IndexOfFrom(commentBlockIndex, COMMENT_BLOCK_END);
184 if (commentBlockIndex < 0)
187 isInCommentBlock =
false;
188 isInDoxygenCommentBlock =
false;
191 trimmedLine = line.Trim();
193 if (isInCommentBlock)
195 if (isInDoxygenCommentBlock)
197 if (currentDoxygenComment)
198 currentDoxygenComment +=
"\n" + trimmedLine;
200 currentDoxygenComment += trimmedLine;
211 if (currentDoxygenComment && trimmedLine.Contains(COMMENT_BLOCK_END))
213 if (trimmedLine == COMMENT_BLOCK_END)
214 currentDoxygenComment +=
"\n" + trimmedLine;
216 if (trimmedLine.StartsWith(COMMENT_BLOCK_END))
217 currentDoxygenComment +=
"\n" + COMMENT_BLOCK_END;
219 currentDoxygenComment +=
"\n" + trimmedLine.Substring(0, trimmedLine.IndexOfFrom(1, COMMENT_BLOCK_END) - 1);
235 if (trimmedLine == METHOD_SEPARATOR)
241 else if (trimmedLine == CONSTRUCTOR_COMMENT || trimmedLine == DESTRUCTOR_COMMENT || trimmedLine.StartsWith(DOXYGEN_LINE_START))
243 if (currentDoxygenComment)
244 currentDoxygenComment +=
"\n" + trimmedLine;
246 currentDoxygenComment = trimmedLine;
258 if (!m_bDoxygenObsoleteMethods && trimmedLine.StartsWith(
"[Obsolete("))
264 if (trimmedLine.StartsWith(
"["))
270 if (trimmedLine.StartsWith(
"class "))
272 string classname = GetClassname(trimmedLine);
275 currentClassName = classname;
281 if (trimmedLine.StartsWith(
"enum "))
287 if (!currentClassName)
290 SCR_DoxygenFillerPlugin_Method method = GetMethodObject(line, lineId);
306 currentDoxygenComment = ConvertDoxygenBlockToSingleLines(currentDoxygenComment, method);
307 if (currentDoxygenComment)
309 currentDoxygenComment = AddIndent(currentDoxygenComment);
310 array<string> doxyBlockLines = {};
311 currentDoxygenComment.Split(
"\n", doxyBlockLines,
false);
312 int doxyBlockLinesCount = doxyBlockLines.Count();
313 if (doxyBlockLinesCount > 0)
315 if (!hasSeparator && doxyBlockLines[0].Trim() == DOXYGEN_LINE_START)
318 commentsToReplace.Insert(
new Tuple2<int, string>(lineId - doxyBlockLines.Count(), currentDoxygenComment));
323 if (!currentDoxygenComment)
326 string doxygenDocumentation = GetDoxygenDocumentation(method, !hasSeparator && m_bAddMissingSeparators, currentClassName);
327 if (doxygenDocumentation)
329 doxygenDocumentation = AddIndent(doxygenDocumentation);
332 Print(
"+Doxygen " + method.m_sName +
"() method (previously on L" + (lineId + 1) +
")",
LogLevel.NORMAL);
333 commentsToAdd.Insert(
new Tuple2<int, string>(lineId + lineOffset, doxygenDocumentation));
340 int addCount = commentsToAdd.Count();
341 int replaceCount = commentsToReplace.Count();
342 if (addCount < 1 && (!m_bConvertDoxygenFormatting || replaceCount < 1))
344 if (m_bConvertDoxygenFormatting)
345 Print(
"No documentable methods nor convertible comments were found",
LogLevel.NORMAL);
347 Print(
"No documentable methods were found",
LogLevel.NORMAL);
352 ScriptEditor scriptEditor = Workbench.GetModule(ScriptEditor);
353 bool useScriptEditor = scriptEditor != null;
360 if (!scriptEditor.SetOpenedResource(filePath))
362 Print(
"Cannot open resource in Script Editor - " + filePath,
LogLevel.WARNING);
366 if (replaceCount > 0)
368 Print(
"Converting " + replaceCount +
" comments...",
LogLevel.NORMAL);
370 array<string> commentLines = {};
371 foreach (Tuple2<int, string> commentToReplace : commentsToReplace)
373 commentToReplace.param2.Split(
"\n", commentLines,
false);
374 foreach (
int i,
string line : commentLines)
376 if (scriptEditor.SetOpenedResource(filePath))
377 scriptEditor.SetLineText(commentLines[i], commentToReplace.param1 + i);
384 Print(
"Documenting " + addCount +
" methods...",
LogLevel.NORMAL);
386 Tuple2<int, string> commentInfo;
387 for (
int i = commentsToAdd.Count() - 1; i > -1; --i)
389 commentInfo = commentsToAdd[i];
390 if (commentInfo.param1 > 0)
392 string trimmedPreviousLine = lines[commentInfo.param1 - 1].Trim();
393 if (trimmedPreviousLine && trimmedPreviousLine !=
"{" && !trimmedPreviousLine.StartsWith(
SCR_StringHelper.DOUBLE_SLASH))
394 commentInfo.param2 =
"\n" + commentInfo.param2;
397 if (scriptEditor.SetOpenedResource(filePath))
398 scriptEditor.InsertLine(commentInfo.param2, commentInfo.param1);
404 if (replaceCount > 0)
411 Tuple2<int, string> commentInfo;
412 for (
int i = commentsToAdd.Count() - 1; i > -1; --i)
414 commentInfo = commentsToAdd[i];
415 if (commentInfo.param1 > 0)
417 string previousLine = lines[commentInfo.param1 - 1].Trim();
419 commentInfo.param2 =
"\n" + commentInfo.param2;
422 lines.InsertAt(commentInfo.param2, commentInfo.param1);
426 if (lines[lines.Count() - 1] !=
string.Empty)
427 lines.Insert(
string.Empty);
440 protected string GetClassname(
string classLine)
445 classLine.Replace(
"\t",
" ");
446 if (!classLine.StartsWith(
"class "))
449 array<string> parts = {};
450 classLine.Split(
" ", parts,
true);
452 if (parts[0] !=
"class")
455 string classname = parts[1];
456 if (classname.EndsWith(
":"))
457 classname = classname.Substring(0, classname.Length() - 1).Trim();
467 protected SCR_DoxygenFillerPlugin_Method GetMethodObject(
string line,
int lineNumber)
472 if (line.StartsWith(
"\treturn"))
475 if (line.Contains(
" = new "))
478 line.Replace(
"\n",
string.Empty);
485 line = line.Substring(0,
index);
487 bool isMethod = line.StartsWith(
"\t") &&
488 !line.StartsWith(
"\t\t") &&
489 line.Contains(
"(") &&
495 string trimmedLine = line.Trim();
500 trimmedLine.Replace(
", ",
",");
503 index = trimmedLine.IndexOf(
"(");
504 array<string> rawSignatureAndParameters = {
505 trimmedLine.Substring(0,
index),
506 trimmedLine.Substring(
index + 1, trimmedLine.Length() -
index - 1),
509 array<string> protectionReturnValueAndName = {};
510 rawSignatureAndParameters[0].Split(
" ", protectionReturnValueAndName,
true);
513 for (
int i = protectionReturnValueAndName.Count() - 2; i >= 0; --i)
515 tempString = protectionReturnValueAndName[i];
516 if (tempString.EndsWith(
"ref"))
518 protectionReturnValueAndName[i] = tempString +
" " + protectionReturnValueAndName[i + 1];
519 protectionReturnValueAndName.RemoveOrdered(i + 1);
523 bool isProtected = protectionReturnValueAndName.RemoveItemOrdered(
"protected");
524 if (!m_bDoxygenProtectedMethods && isProtected)
527 bool isPrivate = !isProtected && protectionReturnValueAndName.RemoveItemOrdered(
"private");
528 if (!m_bDoxygenPrivateMethods && isPrivate)
531 bool isPublic = !isProtected && !isPrivate;
532 if (!m_bDoxygenPublicMethods && isPublic)
535 bool isOverride = protectionReturnValueAndName.RemoveItemOrdered(
"override");
536 if (!m_bDoxygenOverriddenMethods && isOverride)
539 bool isStatic = protectionReturnValueAndName.RemoveItemOrdered(
"static");
540 if (!m_bDoxygenStaticMethods && isStatic)
543 bool isSealed = protectionReturnValueAndName.RemoveItemOrdered(
"sealed");
544 bool isEvent = protectionReturnValueAndName.RemoveItemOrdered(
"event");
546 if (protectionReturnValueAndName.Count() != 2)
548 if (m_bOutputMethodParsingErrors)
554 SCR_DoxygenFillerPlugin_Method method =
new SCR_DoxygenFillerPlugin_Method();
555 method.m_sReturnType = protectionReturnValueAndName[0];
556 method.m_sName = protectionReturnValueAndName[1];
557 method.m_bOverride = isOverride;
558 method.m_bIsStatic = isStatic;
559 method.m_bIsSealed = isSealed;
560 method.m_bIsEvent = isEvent;
563 method.m_iProtection = 1;
565 method.m_iProtection = 2;
569 tempString = rawSignatureAndParameters[1];
570 if (tempString.StartsWith(
")"))
573 tempString = tempString.Substring(0, tempString.LastIndexOf(
")"));
575 array<ref array<string>> paramStrings = GetParamsModifierTypeAndNameFromSig(tempString);
577 SCR_DoxygenFillerPlugin_MethodParameter parameter;
578 foreach (array<string> modifiersTypeAndParamName : paramStrings)
580 parameter =
new SCR_DoxygenFillerPlugin_MethodParameter();
582 if (modifiersTypeAndParamName.RemoveItemOrdered(
"out"))
583 parameter.m_iInOut = 1;
584 else if (modifiersTypeAndParamName.RemoveItemOrdered(
"inout"))
585 parameter.m_iInOut = 2;
587 if (modifiersTypeAndParamName.RemoveItemOrdered(
"notnull"))
588 parameter.m_bNotNull =
true;
590 int count = modifiersTypeAndParamName.Count();
591 if (count != 2 && count != 4)
593 if (m_bOutputMethodParsingErrors)
599 parameter.m_sType = modifiersTypeAndParamName[0];
600 parameter.m_sName = modifiersTypeAndParamName[1];
604 if (modifiersTypeAndParamName[2] ==
"=")
605 parameter.m_sDefaultValue = modifiersTypeAndParamName[3];
607 if (m_bOutputMethodParsingErrors)
611 method.m_aParameters.Insert(parameter);
621 protected array<ref array<string>> GetParamsModifierTypeAndNameFromSig(
string paramsString)
624 paramsString.Replace(
", ",
",");
631 array<ref array<string>> result = {};
632 array<string> subResult = {};
633 for (
int i, count = paramsString.Length(); i < count; i++)
635 currC = paramsString[i];
651 if (chevronLevel == 0)
657 subResult.Insert(buffer);
658 buffer =
string.Empty;
661 result.Insert(subResult);
670 subResult.Insert(buffer);
671 buffer =
string.Empty;
680 else if (currC ==
">")
687 subResult.Insert(buffer);
689 if (!subResult.IsEmpty())
690 result.Insert(subResult);
693 foreach (array<string> subArray : result)
695 foreach (
int i,
string value : subArray)
697 value.Replace(
",",
", ");
706 protected string GetDoxygenDocumentation(notnull SCR_DoxygenFillerPlugin_Method method,
bool addSeparator =
false,
string classname =
string.Empty)
708 string generatedDoxygenDocumentation;
710 bool isConstrOrDestr;
711 if (classname && method.m_sReturnType ==
"void")
713 if (m_bAddDestructorNormalComment && method.m_sName ==
"~" + classname)
715 generatedDoxygenDocumentation = DESTRUCTOR_COMMENT;
716 isConstrOrDestr =
true;
719 if (m_bAddConstructorNormalComment && method.m_sName == classname)
721 generatedDoxygenDocumentation = CONSTRUCTOR_COMMENT;
722 isConstrOrDestr =
true;
726 if (!isConstrOrDestr)
729 generatedDoxygenDocumentation += DOXYGEN_LINE_START;
732 foreach (SCR_DoxygenFillerPlugin_MethodParameter parameter : method.m_aParameters)
734 if (generatedDoxygenDocumentation)
735 generatedDoxygenDocumentation +=
"\n";
737 generatedDoxygenDocumentation += DOXYGEN_LINE_START +
" \\param";
738 switch (parameter.m_iInOut)
740 case 0: generatedDoxygenDocumentation +=
"[in]";
break;
741 case 1: generatedDoxygenDocumentation +=
"[out]";
break;
742 case 2: generatedDoxygenDocumentation +=
"[in,out]";
break;
745 generatedDoxygenDocumentation +=
" " + parameter.m_sName;
748 if (method.m_sReturnType !=
"void")
750 if (generatedDoxygenDocumentation)
751 generatedDoxygenDocumentation +=
"\n";
753 generatedDoxygenDocumentation += DOXYGEN_LINE_START +
" \\return";
758 if (generatedDoxygenDocumentation)
759 generatedDoxygenDocumentation = METHOD_SEPARATOR +
"\n" + generatedDoxygenDocumentation;
761 generatedDoxygenDocumentation = METHOD_SEPARATOR;
764 return generatedDoxygenDocumentation;
773 protected string ConvertDoxygenBlockToSingleLines(
string doxygenBlock, inout SCR_DoxygenFillerPlugin_Method method)
775 doxygenBlock.TrimInPlace();
782 int length = doxygenBlock.Length();
785 else if (length == 5)
786 return DOXYGEN_LINE_START;
793 int count = lines.Count();
798 return lines[0].Substring(3, lines[0].Length() - 5);
800 string line = lines[0];
801 length = line.Length();
803 lines[0] =
string.Empty;
805 lines[0] = line.Substring(3, length - 3);
807 line = lines[count - 1];
808 length = line.Length();
810 lines[count - 1] =
string.Empty;
812 lines[count - 1] = line.Substring(0, length - 2).Trim();
814 foreach (
int i,
string line2 : lines)
820 SCR_DoxygenFillerPlugin_MethodParameter param;
821 for (
int j = method.m_aParameters.Count() - 1; j >= 0; --j)
823 param = method.m_aParameters[j];
824 if (line2.StartsWith(FAULTY_PARAM + param.m_sName))
827 switch (param.m_iInOut)
829 case 0: suffix =
"[in]";
break;
830 case 1: suffix =
"[out]";
break;
831 case 2: suffix =
"[in,out]";
break;
834 line2.Replace(FAULTY_PARAM + param.m_sName, BASE_PARAM + suffix +
" " + param.m_sName);
840 lines[i] = DOXYGEN_LINE_START +
" " + line2;
844 lines[i] = DOXYGEN_LINE_START;
857 protected static string AddIndent(
string input,
int indentLevel = 1,
string indent =
"\t")
866 input.Replace(
"\n",
"\n" + indent);
867 return indent + input;
874 Workbench.ScriptDialog(
"Configure Doxygen plugin",
"Add Doxygen skeleton where wanted and needed.",
this);
905class SCR_DoxygenFillerPlugin_Method
912 string m_sReturnType;
914 ref array<ref SCR_DoxygenFillerPlugin_MethodParameter> m_aParameters = {};
917class SCR_DoxygenFillerPlugin_MethodParameter
923 string m_sDefaultValue;
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
static bool WriteFileContent(string filePath, notnull array< string > lines)
static array< string > ReadFileContent(string filePath, bool printWarning=true)
static array< string > GetLines(string input, bool removeEmptyLines=false, bool trimLines=false)
static string PadLeft(string input, int length, string padding=SPACE)
static string ReplaceRecursive(string input, string sample, string replacement)
static int CountOccurrences(string haystack, string needle, bool caseInsensitive=false)
static int IndexOfFrom(string input, int start, notnull array< string > samples)
static bool StartsWithAny(string input, notnull array< string > lineStarts)
static string ReplaceTimes(string input, string sample, string replacement, int howMany=1, int skip=0)
static bool IsEmptyOrWhiteSpace(string input)
static string Join(string separator, notnull array< string > pieces, bool joinEmptyEntries=true)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
SCR_FieldOfViewSettings Attribute