public void HighlightTest() { string initString = "// This is highlighted string"; int prevLength = initString.Length; SyntaxTheme theme = ScriptableObject.CreateInstance <SyntaxTheme>(); string testString = SyntaxHighlighter.HighlightCode(initString, theme); int finalLength = testString.Length; Assert.AreNotEqual(string.Empty, testString); Assert.AreNotEqual(prevLength, finalLength); Assert.AreNotEqual(initString, testString); }
public static string HighlightCode(string code, SyntaxTheme theme) { string highlightedCode = ""; string[] lines = code.Split('\n'); for (int i = 0; i < lines.Length; i++) { ; highlightedCode += HighlightLine(lines[i], theme); if (i != lines.Length - 1) { highlightedCode += '\n'; } } return(highlightedCode); }
// Highlights in-game written code with a given theme public static string HighlightCode(string code, SyntaxTheme theme) { string highlightedCode = ""; // Init string string[] codeLines = code.Split('\n'); // Split by new line // Iterate over each line, adding a highlighted line (Unity's rich text) to a string for (int i = 0; i < codeLines.Length; i++) { highlightedCode += Highlight(codeLines[i], theme); if (i != codeLines.Length - 1) // if i does not exceed array length, add a new line { highlightedCode += '\n'; } } return(highlightedCode); // Return highlighted code }
static string HighlightLine(string line, SyntaxTheme theme) { var cols = new List <Color>(); var startEndIndices = new List <Vector2Int>(); string[] sections = CompilerFormatter.Format(line).Split(' '); int nonSpaceIndex = 0; bool isComment = false; for (int i = 0; i < sections.Length; i++) { Color colour = Color.clear; string section = sections[i]; if (section == CompilerSymbols.commentMarker || isComment) { colour = theme.comment; isComment = true; } else if (section == CompilerSymbols.usingKeyword) { colour = theme.usingKeyword; } else if (CompilerSymbols.ArrayContainsString(CompilerSymbols.types, section)) { colour = theme.types; } else if (CompilerSymbols.ArrayContainsString(CompilerSymbols.reservedNames, section)) { colour = theme.reservedNames; } else if (CompilerSymbols.allOperators.Contains(section.ToLower())) { colour = theme.allOperators; } else if (CompilerSymbols.ArrayContainsString(CompilerSymbols.builtinFunctions, section)) { colour = theme.builtinFunctions; } else if (CompilerSymbols.brackets.Contains(section.ToLower())) { colour = theme.brackets; } else if (float.TryParse(section.Replace('.', ','), out _)) { colour = theme.value; } else { colour = theme.variable; } if (colour != Color.clear) { cols.Add(colour); int endIndex = nonSpaceIndex + sections[i].Length; startEndIndices.Add(new Vector2Int(nonSpaceIndex, endIndex)); } nonSpaceIndex += sections[i].Length; } if (cols.Count > 0) { nonSpaceIndex = 0; int colIndex = 0; int actualStartIndex = -1; for (int i = 0; i <= line.Length; i++) { if (startEndIndices[colIndex].x == nonSpaceIndex) { actualStartIndex = i; } else if (startEndIndices[colIndex].y == nonSpaceIndex) { //print (colIndex + " replace: " + startEndIndices[colIndex] +" with: " + new Vector2Int (actualStartIndex, i)); startEndIndices[colIndex] = new Vector2Int(actualStartIndex, i); colIndex++; if (colIndex >= cols.Count) { break; } i--; continue; } if (i < line.Length) { char c = line[i]; if (c != ' ') { nonSpaceIndex++; } } } } for (int i = cols.Count - 1; i >= 0; i--) { var col = cols[i]; var startEndIndex = startEndIndices[i]; string colString = ColorUtility.ToHtmlStringRGB(col); line = line.Insert(startEndIndex.y, "</color>"); line = line.Insert(startEndIndex.x, $"<color=#{colString}>"); //print ("insert: " + startEndIndex.x + " " + startEndIndex.y); } return(line); }
static string HighlightLine(string line, SyntaxTheme theme) { var cols = new List <Color> (); var startEndIndices = new List <Vector2Int> (); string[] sections = CompilerFormatter.Format(line).Split(' '); int nonSpaceIndex = 0; bool isComment = false; for (int i = 0; i < sections.Length; i++) { Color colour = Color.clear; string section = sections[i]; if (section == VirtualCompiler.commentMarker || isComment) { colour = theme.comment; isComment = true; } else if (section == "if" || section == "elseif" || section == "else") { colour = theme.conditionKeyword; } else if (section == "loop") { colour = theme.loopKeyword; } else if (VirtualCompiler.ArrayContainsString(VirtualCompiler.mathOperators, section)) { colour = theme.mathOperator; } else if (VirtualCompiler.ArrayContainsString(VirtualCompiler.comparisonOperators, section)) { colour = theme.comparisonOperator; } else if (section == "(" || section == ")") { colour = theme.parentheses; } else if (section == "{" || section == "}") { colour = theme.curlyBrackets; } else if (float.TryParse(section.Replace('.', ','), out _)) { colour = theme.value; } else if (section == "=") { colour = theme.assignment; } else { colour = theme.variable; } if (colour != Color.clear) { cols.Add(colour); int endIndex = nonSpaceIndex + sections[i].Length; startEndIndices.Add(new Vector2Int(nonSpaceIndex, endIndex)); } nonSpaceIndex += sections[i].Length; } if (cols.Count > 0) { nonSpaceIndex = 0; int colIndex = 0; int actualStartIndex = -1; for (int i = 0; i <= line.Length; i++) { if (startEndIndices[colIndex].x == nonSpaceIndex) { actualStartIndex = i; } else if (startEndIndices[colIndex].y == nonSpaceIndex) { //print (colIndex + " replace: " + startEndIndices[colIndex] +" with: " + new Vector2Int (actualStartIndex, i)); startEndIndices[colIndex] = new Vector2Int(actualStartIndex, i); colIndex++; if (colIndex >= cols.Count) { break; } i--; continue; } if (i < line.Length) { char c = line[i]; if (c != ' ') { nonSpaceIndex++; } } } } for (int i = cols.Count - 1; i >= 0; i--) { var col = cols[i]; var startEndIndex = startEndIndices[i]; string colString = ColorUtility.ToHtmlStringRGB(col); line = line.Insert(startEndIndex.y, "</color>"); line = line.Insert(startEndIndex.x, $"<color=#{colString}>"); //print ("insert: " + startEndIndex.x + " " + startEndIndex.y); } return(line); }
// Highlights a line static string Highlight(string line, SyntaxTheme theme) { List <Color> colors = new List <Color>(); // Init list of colors List <Vector2Int> wordStartEnd = new List <Vector2Int>(); // Init list of integers - indices of where words start & end int notSpace = 0; // Where does a word start? // Divide a code by breakChars (e.g. stop characters, commas, parentheses, brackets) // This allows me to highlight code even if it is a single word // E.g. by default, a word "Debug" would be highlighted, but not in a word "Debug.Log()" // This is because a program had seen it as a single word // What I'm essentially doing here is converting "Debug.Log()" to something like "Debug . Log ( )" // By putting spaces in between characters, a program started picking it up correctly // The code still compiles string subLine = ""; subLine = line; for (int i = 0; i < subLine.Length; i++) { char currentChar = subLine[i]; for (int j = 0; j < Keywords.breakChars.Length; j++) { string current = Keywords.breakChars[j]; if (currentChar.ToString() == current) { if (subLine.Length > 0) { if ((i - 1 != -1) && subLine[i - 1].ToString() != " ") { subLine = subLine.Insert(i, " "); } if (i + 1 < subLine.Length && subLine[i + 1].ToString() != " ") { subLine = subLine.Insert(i + 1, " "); } } } } } // Split words by spaces string[] words = subLine.Split(' '); // Highlight words for (int i = 0; i < words.Length; i++) { Color c = Color.clear; // Highlight comment lines if (words[i].StartsWith(Keywords.commentMark) == true) { line = line.TrimEnd(' '); int beginning = line.IndexOf('/'); int end = line.Length; wordStartEnd.Add(new Vector2Int(beginning, end)); c = theme.commentColor; colors.Add(c); break; } // Highlight strings & single characters (words between '' and ""); if (words[i].StartsWith(Keywords.charMark) || words[i].StartsWith(Keywords.quotationMark)) { c = theme.stringColor; } // Highlight C# keywords (e.g. int) for (int j = 0; j < Keywords.blueWords.Length; j++) { string word = Keywords.blueWords[j]; if (words[i] == word) { c = theme.variableColor; } } // Highlight Unity keywords (e.g. "Update") for (int j = 0; j < Keywords.unityKeywords.Length; j++) { string word = Keywords.unityKeywords[j]; if (words[i] == word) { c = theme.unityKeyword; } } // Highlight Unity classes names (e.g. "Debug") for (int j = 0; j < Keywords.unityClasses.Length; j++) { string word = Keywords.unityClasses[j]; if (words[i] == word) { c = theme.unityClass; } } // Highlight custom classes made by me (e.g. "EventTrigger") for (int j = 0; j < Keywords.customClasses.Length; j++) { string word = Keywords.customClasses[j]; if (words[i] == word) { c = theme.unityClass; } } // If a word has been highlighted, add its indices and colors to the list if (c != Color.clear) { colors.Add(c); int end = notSpace + words[i].Length; wordStartEnd.Add(new Vector2Int(notSpace, end)); } notSpace += words[i].Length; // Increment notSpace by word's length } if (colors.Count > 0) // If a line is to be highlighted { notSpace = 0; int colorIndex = 0; int actualStartIndex = -1; for (int i = 0; i <= line.Length; i++) { if (wordStartEnd[colorIndex].x == notSpace) // check for actual start and end position { actualStartIndex = i; } else if (wordStartEnd[colorIndex].y == notSpace) // If there is an end tag (</color>) in the middle of the word { wordStartEnd[colorIndex] = new Vector2Int(actualStartIndex, i); colorIndex++; if (colorIndex >= colors.Count) // if colorIndex exceeded number of colors, break { break; } i--; // decrement i continue; } if (i < line.Length) // look for spaces in a line { char character = line[i]; if (character != ' ') { notSpace++; } } } } for (int i = colors.Count - 1; i >= 0; i--) // Insert rich text in a string { var color = colors[i]; var startEnd = wordStartEnd[i]; string colorString = ColorUtility.ToHtmlStringRGB(color); // Convert a color to a string line = line.Insert(startEnd.y, "</color>"); line = line.Insert(startEnd.x, $"<color=#{colorString}>"); } return(line); }