using System.Linq; using System.Text; namespace Unity.Tutorials.Core.Editor { internal static class CodeSampleUtils { /// /// Returns the string formatted as code (indented) /// /// /// The given string as tabulated formated code public static string AsFormattedCode(string originalString) { var lines = originalString.Split('\n').Select(s => s.Trim()); var strBuilder = new StringBuilder(); int indentCount = 0; bool shouldIndent = false; foreach (string line in lines) { if (shouldIndent) indentCount++; if (line.Trim() == "}") indentCount--; if (indentCount == 0) { strBuilder.AppendLine(line); shouldIndent = line.Contains("{"); continue; } string blankSpace = string.Empty; for (int i = 0; i < indentCount; i++) { blankSpace += " "; } if (line.Contains("}") && line.Trim() != "}") indentCount--; strBuilder.AppendLine(blankSpace + line); shouldIndent = line.Contains("{"); } return strBuilder.ToString().Trim(); } /// /// Add rich text tag to color the given code. This only look for a subset of keywords, comments and function /// /// /// A string containing the code with added rich text tag with color public static string HighlightCode(string code) { var strBuilder = new StringBuilder(); var currentToken = new StringBuilder(); //as we use rich text tag, we cannot use USS class, so we have to fix them in code. const string keywordColor = "#6C95EB"; const string funcColor = "#39CC8F"; const string stringColor = "#C9A26D"; const string commentColor = "#85C46C"; var keyword = new string[] { "bool", "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "double", "float", "decimal", "char", "var", "string", "char", "void", "object", "typeof", "sizeof", "null", "true", "false", "if", "else", "while", "for", "foreach", "do", "switch", "case", "default", "lock", "try", "throw", "catch", "finally", "goto", "break", "continue", "return", "public", "private", "internal", "protected", "static", "readonly", "sealed", "const", "fixed", "stackalloc", "volatile", "new", "override", "abstract", "virtual", "event", "extern", "ref", "out", "in", "is", "as", "params", "__arglist", "__makeref", "__reftype", "__refvalue", "this", "base", "namespace", "using", "class", "struct", "interface", "enum", "delegate", "checked", "unchecked", "unsafe", "operator", "implicit", "explicit" }; var separator = new char[] { '(', ')', '{', '}', '[', ']', '.', ',', ';'}; char previousCharacter = ' '; bool singleLineCommentOpened = false; bool multiLineCommentOpened = false; foreach (var c in code) { if (multiLineCommentOpened) { //for multiline comments, we only care about the comment block closing, so we just append the character //to the token until we encounter the closing character currentToken.Append(c); if (c == '/' && previousCharacter == '*') { multiLineCommentOpened = false; currentToken.Insert(0, $""); currentToken.Append(""); strBuilder.Append(currentToken); currentToken.Clear(); } } else { if (c == '*' && previousCharacter == '/') { //opening a block comment, we ignore everything else from now on multiLineCommentOpened = true; currentToken.Append(c); } //the character is a white space (space, line return etc.) else if (char.IsWhiteSpace(c) || separator.Any(c1 => c1 == c)) { //if we have a single comment type opened, this may mean we need to close it if (singleLineCommentOpened) { //we finished a line, so we do close the comment and color the current token (which will be the full line) if (c == '\r' || c == '\n') { currentToken.Insert(0, $""); currentToken.Append(""); singleLineCommentOpened = false; strBuilder.Append(currentToken); strBuilder.Append(c); currentToken.Clear(); } else { //we add the character to the token, as for a single line comment, everything will be colored currentToken.Append(c); } } else { var token = currentToken.ToString(); if (keyword.Any(s => token == s)) { currentToken.Insert(0, $""); currentToken.Append(""); } else if (c == '(') { currentToken.Insert(0, $""); currentToken.Append(""); } else if (token.EndsWith('"')) { currentToken.Insert(0, $""); currentToken.Append(""); } strBuilder.Append(currentToken); strBuilder.Append(c); currentToken.Clear(); } } else if (c == '/' && previousCharacter == '/') { //we are opening a line comment, ignore until we encounter a end of line currentToken.Append(c); singleLineCommentOpened = true; } else { currentToken.Append(c); } } previousCharacter = c; } return strBuilder.ToString(); } } }