cleanup and refactor C# lexer classes

This commit is contained in:
sinaioutlander 2020-11-11 00:16:01 +11:00
parent 9c077b3aa3
commit 70a1570441
13 changed files with 308 additions and 506 deletions

View File

@ -133,7 +133,7 @@ namespace UnityExplorer.Console
{ {
var editor = ConsolePage.Instance.m_codeEditor; var editor = ConsolePage.Instance.m_codeEditor;
var textGen = editor.inputText.cachedTextGenerator; var textGen = editor.InputText.cachedTextGenerator;
//if (textGen.characters.Count < 1) //if (textGen.characters.Count < 1)
// return; // return;

View File

@ -1,19 +1,45 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using UnityExplorer.Console.Lexer;
using UnityEngine; using UnityEngine;
using UnityExplorer.Console.Lexer;
namespace UnityExplorer.Console namespace UnityExplorer.Console
{ {
public static class CSharpLexer public struct LexerMatchInfo
{ {
public static char indentIncreaseCharacter = '{'; public int startIndex;
public static char indentDecreaseCharacter = '}'; public int endIndex;
public string htmlColor;
}
public static string delimiterSymbols = "[ ] ( ) { } ; : , ."; public enum DelimiterType
{
Start,
End,
};
public class CSharpLexer
{
private string inputString;
private readonly Matcher[] matchers;
private readonly HashSet<char> startDelimiters;
private readonly HashSet<char> endDelimiters;
private int currentIndex;
private int currentLookaheadIndex;
public char Current { get; private set; } = ' ';
public char Previous { get; private set; } = ' ';
public bool EndOfStream => currentLookaheadIndex >= inputString.Length;
public static char indentOpen = '{';
public static char indentClose = '}';
private static readonly StringBuilder indentBuilder = new StringBuilder(); private static readonly StringBuilder indentBuilder = new StringBuilder();
public static char[] delimiters = new[]
{
'[', ']', '(', ')', '{', '}', ';', ':', ',', '.'
};
public static CommentMatch commentMatcher = new CommentMatch(); public static CommentMatch commentMatcher = new CommentMatch();
public static SymbolMatch symbolMatcher = new SymbolMatch(); public static SymbolMatch symbolMatcher = new SymbolMatch();
public static NumberMatch numberMatcher = new NumberMatch(); public static NumberMatch numberMatcher = new NumberMatch();
@ -22,65 +48,30 @@ namespace UnityExplorer.Console
public static KeywordMatch validKeywordMatcher = new KeywordMatch public static KeywordMatch validKeywordMatcher = new KeywordMatch
{ {
highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f), highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f),
keywords = @"add as ascending await bool break by byte Keywords = new[] { "add", "as", "ascending", "await", "bool", "break", "by", "byte",
case catch char checked const continue decimal default descending do dynamic "case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic",
else equals false finally float for foreach from global goto group "else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group",
if in int into is join let lock long new null object on orderby out "if", "in", "int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out",
ref remove return sbyte select short sizeof stackalloc string "ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string",
switch throw true try typeof uint ulong ushort "switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield" }
var where while yield"
}; };
public static KeywordMatch invalidKeywordMatcher = new KeywordMatch() public static KeywordMatch invalidKeywordMatcher = new KeywordMatch()
{ {
highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f), highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f),
keywords = @"abstract async base class delegate enum explicit extern fixed get Keywords = new[] { "abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
implicit interface internal namespace operator override params private protected public "implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
using partial readonly sealed set static struct this unchecked unsafe value virtual volatile void" "using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" }
}; };
private static char[] delimiterSymbolCache = null; // ~~~~~~~ ctor ~~~~~~~
internal static char[] DelimiterSymbols
{
get
{
if (delimiterSymbolCache == null)
{
string[] symbols = delimiterSymbols.Split(' ');
int count = 0; public CSharpLexer()
{
startDelimiters = new HashSet<char>(delimiters);
endDelimiters = new HashSet<char>(delimiters);
for (int i = 0; i < symbols.Length; i++) this.matchers = new Matcher[]
{
if (symbols[i].Length == 1)
{
count++;
}
}
delimiterSymbolCache = new char[count];
for (int i = 0, index = 0; i < symbols.Length; i++)
{
if (symbols[i].Length == 1)
{
delimiterSymbolCache[index] = symbols[i][0];
index++;
}
}
}
return delimiterSymbolCache;
}
}
private static Matcher[] matchers = null;
internal static Matcher[] Matchers
{
get
{
if (matchers == null)
{
List<Matcher> matcherList = new List<Matcher>
{ {
commentMatcher, commentMatcher,
symbolMatcher, symbolMatcher,
@ -90,11 +81,75 @@ namespace UnityExplorer.Console
invalidKeywordMatcher, invalidKeywordMatcher,
}; };
matchers = matcherList.ToArray(); foreach (Matcher lexer in matchers)
{
foreach (char c in lexer.StartChars)
{
if (!startDelimiters.Contains(c))
startDelimiters.Add(c);
} }
return matchers;
foreach (char c in lexer.EndChars)
{
if (!endDelimiters.Contains(c))
endDelimiters.Add(c);
} }
} }
}
// ~~~~~~~ Lex Matching ~~~~~~~
public IEnumerable<LexerMatchInfo> GetMatches(string input)
{
if (input == null || matchers == null || matchers.Length == 0)
{
yield break;
}
inputString = input;
Current = ' ';
Previous = ' ';
currentIndex = 0;
currentLookaheadIndex = 0;
while (!EndOfStream)
{
bool didMatchLexer = false;
ReadWhiteSpace();
foreach (Matcher matcher in matchers)
{
int startIndex = currentIndex;
bool isMatched = matcher.IsMatch(this);
if (isMatched)
{
int endIndex = currentIndex;
didMatchLexer = true;
yield return new LexerMatchInfo
{
startIndex = startIndex,
endIndex = endIndex,
htmlColor = matcher.HexColor,
};
break;
}
}
if (!didMatchLexer)
{
ReadNext();
Commit();
}
}
}
// ~~~~~~~ Indent ~~~~~~~
public static string GetIndentForInput(string input, int indent, out int caretPosition) public static string GetIndentForInput(string input, int indent, out int caretPosition)
{ {
@ -123,14 +178,14 @@ namespace UnityExplorer.Console
{ {
continue; continue;
} }
else if (!stringState && input[i] == indentIncreaseCharacter) else if (!stringState && input[i] == indentOpen)
{ {
indentBuilder.Append(indentIncreaseCharacter); indentBuilder.Append(indentOpen);
indent++; indent++;
} }
else if (!stringState && input[i] == indentDecreaseCharacter) else if (!stringState && input[i] == indentClose)
{ {
indentBuilder.Append(indentDecreaseCharacter); indentBuilder.Append(indentClose);
indent--; indent--;
} }
else else
@ -177,5 +232,77 @@ namespace UnityExplorer.Console
return indent; return indent;
} }
// Lexer reading
public char ReadNext()
{
if (EndOfStream)
{
return '\0';
}
Previous = Current;
Current = inputString[currentLookaheadIndex];
currentLookaheadIndex++;
return Current;
}
public void Rollback(int amount = -1)
{
if (amount == -1)
{
currentLookaheadIndex = currentIndex;
}
else
{
if (currentLookaheadIndex > currentIndex)
{
currentLookaheadIndex -= amount;
}
}
int previousIndex = currentLookaheadIndex - 1;
if (previousIndex >= inputString.Length)
{
Previous = inputString[inputString.Length - 1];
}
else if (previousIndex >= 0)
{
Previous = inputString[previousIndex];
}
else
{
Previous = ' ';
}
}
public void Commit()
{
currentIndex = currentLookaheadIndex;
}
public bool IsSpecialSymbol(char character, DelimiterType position = DelimiterType.Start)
{
if (position == DelimiterType.Start)
{
return startDelimiters.Contains(character);
}
return endDelimiters.Contains(character);
}
private void ReadWhiteSpace()
{
while (char.IsWhiteSpace(ReadNext()) == true)
{
Commit();
}
Rollback();
}
} }
} }

View File

@ -18,22 +18,21 @@ using UnhollowerRuntimeLib;
namespace UnityExplorer.Console namespace UnityExplorer.Console
{ {
// Handles most of the UI side of the C# console, including syntax highlighting.
public class CodeEditor public class CodeEditor
{ {
private readonly InputLexer inputLexer = new InputLexer();
public InputField InputField { get; internal set; } public InputField InputField { get; internal set; }
public Text InputText { get; internal set; }
public int CurrentIndent { get; private set; }
public Text inputText;
private Text inputHighlightText; private Text inputHighlightText;
private Image background; private Image background;
private Image scrollbar; private Image scrollbar;
public int LineCount { get; private set; } public string HighlightedText => inputHighlightText.text;
public int CurrentLine { get; private set; } private readonly CSharpLexer highlightLexer;
public int CurrentIndent { get; private set; } private readonly StringBuilder sbHighlight;
private static readonly StringBuilder highlightedBuilder = new StringBuilder(4096);
private static readonly KeyCode[] onFocusKeys = private static readonly KeyCode[] onFocusKeys =
{ {
@ -41,28 +40,6 @@ namespace UnityExplorer.Console
KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow
}; };
public string HighlightedText => inputHighlightText.text;
public string Text
{
get { return InputField.text; }
set
{
if (!string.IsNullOrEmpty(value))
{
InputField.text = value;
inputHighlightText.text = value;
}
else
{
InputField.text = string.Empty;
inputHighlightText.text = string.Empty;
}
//inputText.ForceMeshUpdate(false);
}
}
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console. internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
The following helper methods are available: The following helper methods are available:
@ -86,14 +63,14 @@ The following helper methods are available:
public CodeEditor() public CodeEditor()
{ {
ConstructUI(); sbHighlight = new StringBuilder();
highlightLexer = new CSharpLexer();
ApplyTheme(); ConstructUI();
inputLexer.UseMatchers(CSharpLexer.DelimiterSymbols, CSharpLexer.Matchers);
// subscribe to text input changing // subscribe to text input changing
#if CPP #if CPP
InputField.onValueChanged.AddListener(new Action<string>((string s) => { OnInputChanged(); })); InputField.onValueChanged.AddListener(new Action<string>((string s) => { OnInputChanged(s); }));
#else #else
this.InputField.onValueChanged.AddListener((string s) => { OnInputChanged(s); }); this.InputField.onValueChanged.AddListener((string s) => { OnInputChanged(s); });
#endif #endif
@ -166,10 +143,10 @@ The following helper methods are available:
{ {
char character = newText[i]; char character = newText[i];
if (character == CSharpLexer.indentIncreaseCharacter) if (character == CSharpLexer.indentOpen)
CurrentIndent++; CurrentIndent++;
if (character == CSharpLexer.indentDecreaseCharacter) if (character == CSharpLexer.indentClose)
CurrentIndent--; CurrentIndent--;
} }
@ -183,33 +160,33 @@ The following helper methods are available:
{ {
int offset = 0; int offset = 0;
highlightedBuilder.Length = 0; sbHighlight.Length = 0;
foreach (LexerMatchInfo match in inputLexer.LexInputString(inputText)) foreach (LexerMatchInfo match in highlightLexer.GetMatches(inputText))
{ {
for (int i = offset; i < match.startIndex; i++) for (int i = offset; i < match.startIndex; i++)
{ {
highlightedBuilder.Append(inputText[i]); sbHighlight.Append(inputText[i]);
} }
highlightedBuilder.Append($"{match.htmlColor}"); sbHighlight.Append($"{match.htmlColor}");
for (int i = match.startIndex; i < match.endIndex; i++) for (int i = match.startIndex; i < match.endIndex; i++)
{ {
highlightedBuilder.Append(inputText[i]); sbHighlight.Append(inputText[i]);
} }
highlightedBuilder.Append(CLOSE_COLOR_TAG); sbHighlight.Append(CLOSE_COLOR_TAG);
offset = match.endIndex; offset = match.endIndex;
} }
for (int i = offset; i < inputText.Length; i++) for (int i = offset; i < inputText.Length; i++)
{ {
highlightedBuilder.Append(inputText[i]); sbHighlight.Append(inputText[i]);
} }
inputText = highlightedBuilder.ToString(); inputText = sbHighlight.ToString();
return inputText; return inputText;
} }
@ -242,8 +219,8 @@ The following helper methods are available:
} }
// check if should add auto-close } // check if should add auto-close }
int numOpen = InputField.text.Where(x => x == CSharpLexer.indentIncreaseCharacter).Count(); int numOpen = InputField.text.Where(x => x == CSharpLexer.indentOpen).Count();
int numClose = InputField.text.Where(x => x == CSharpLexer.indentDecreaseCharacter).Count(); int numClose = InputField.text.Where(x => x == CSharpLexer.indentClose).Count();
if (numOpen > numClose) if (numOpen > numClose)
{ {
@ -263,13 +240,13 @@ The following helper methods are available:
// Update line column and indent positions // Update line column and indent positions
UpdateIndent(InputField.text); UpdateIndent(InputField.text);
inputText.text = InputField.text; InputText.text = InputField.text;
//inputText.SetText(InputField.text, true); //inputText.SetText(InputField.text, true);
inputText.Rebuild(CanvasUpdate.Prelayout); InputText.Rebuild(CanvasUpdate.Prelayout);
InputField.ForceLabelUpdate(); InputField.ForceLabelUpdate();
InputField.Rebuild(CanvasUpdate.Prelayout); InputField.Rebuild(CanvasUpdate.Prelayout);
OnInputChanged(inputText.text, true); OnInputChanged(InputText.text, true);
} }
private string GetAutoIndentTab(int amount) private string GetAutoIndentTab(int amount)
@ -284,26 +261,6 @@ The following helper methods are available:
return tab; return tab;
} }
// ============== Theme ============== //
private static Color backgroundColor = new Color32(37, 37, 37, 255);
private static Color scrollbarColor = new Color32(45, 50, 50, 255);
private void ApplyTheme()
{
var highlightTextRect = inputHighlightText.GetComponent<RectTransform>();
highlightTextRect.anchorMin = Vector2.zero;
highlightTextRect.anchorMax = Vector2.one;
highlightTextRect.offsetMin = Vector2.zero;
highlightTextRect.offsetMax = Vector2.zero;
InputField.caretColor = Color.white;
inputText.color = new Color(1, 1, 1, 0.51f);
inputHighlightText.color = Color.white;
background.color = backgroundColor;
scrollbar.color = scrollbarColor;
}
// ========== UI CONSTRUCTION =========== // // ========== UI CONSTRUCTION =========== //
public void ConstructUI() public void ConstructUI()
@ -523,14 +480,27 @@ The following helper methods are available:
mainTextInput.font = UIManager.ConsoleFont; mainTextInput.font = UIManager.ConsoleFont;
highlightTextInput.font = UIManager.ConsoleFont; highlightTextInput.font = UIManager.ConsoleFont;
// reset this after formatting finalized
highlightTextRect.anchorMin = Vector2.zero;
highlightTextRect.anchorMax = Vector2.one;
highlightTextRect.offsetMin = Vector2.zero;
highlightTextRect.offsetMax = Vector2.zero;
// assign references // assign references
this.InputField = inputField; this.InputField = inputField;
this.inputText = mainTextInput; this.InputText = mainTextInput;
this.inputHighlightText = highlightTextInput; this.inputHighlightText = highlightTextInput;
this.background = mainBgImage; this.background = mainBgImage;
this.scrollbar = scrollImage; this.scrollbar = scrollImage;
// set some colors
InputField.caretColor = Color.white;
InputText.color = new Color(1, 1, 1, 0.51f);
inputHighlightText.color = Color.white;
background.color = new Color32(37, 37, 37, 255);
scrollbar.color = new Color32(45, 50, 50, 255);
} }
} }
} }

View File

@ -3,7 +3,7 @@ using UnityEngine;
namespace UnityExplorer.Console.Lexer namespace UnityExplorer.Console.Lexer
{ {
public sealed class CommentMatch : Matcher public class CommentMatch : Matcher
{ {
public string lineCommentStart = @"//"; public string lineCommentStart = @"//";
public string blockCommentStart = @"/*"; public string blockCommentStart = @"/*";
@ -12,9 +12,9 @@ namespace UnityExplorer.Console.Lexer
public override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f); public override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f);
public override IEnumerable<char> StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] }; public override IEnumerable<char> StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] };
public override IEnumerable<char> EndChars => new char[] { blockCommentEnd[0] }; public override IEnumerable<char> EndChars => new char[] { blockCommentEnd[0] };
public override bool IsImplicitMatch(InputLexer lexer) => IsMatch(lexer, lineCommentStart) || IsMatch(lexer, blockCommentStart); public override bool IsImplicitMatch(CSharpLexer lexer) => IsMatch(lexer, lineCommentStart) || IsMatch(lexer, blockCommentStart);
private bool IsMatch(InputLexer lexer, string commentType) private bool IsMatch(CSharpLexer lexer, string commentType)
{ {
if (!string.IsNullOrEmpty(commentType)) if (!string.IsNullOrEmpty(commentType))
{ {
@ -32,11 +32,8 @@ namespace UnityExplorer.Console.Lexer
if (match) if (match)
{ {
// Read until end // Read until end of line or file
while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) { }
{
;
}
return true; return true;
} }
@ -44,6 +41,6 @@ namespace UnityExplorer.Console.Lexer
return false; return false;
} }
private bool IsEndLineOrEndFile(InputLexer lexer, char character) => lexer.EndOfStream || character == '\n' || character == '\r'; private bool IsEndLineOrEndFile(CSharpLexer lexer, char character) => lexer.EndOfStream || character == '\n' || character == '\r';
} }
} }

View File

@ -1,201 +0,0 @@
using System.Collections.Generic;
namespace UnityExplorer.Console.Lexer
{
public struct LexerMatchInfo
{
public int startIndex;
public int endIndex;
public string htmlColor;
}
public enum SpecialCharacterPosition
{
Start,
End,
};
public class InputLexer
{
private string inputString = null;
private Matcher[] matchers = null;
private readonly HashSet<char> specialStartSymbols = new HashSet<char>();
private readonly HashSet<char> specialEndSymbols = new HashSet<char>();
private int currentIndex = 0;
private int currentLookaheadIndex = 0;
private char current = ' ';
public char Previous { get; private set; } = ' ';
public bool EndOfStream
{
get { return currentLookaheadIndex >= inputString.Length; }
}
public void UseMatchers(char[] delimiters, Matcher[] matchers)
{
this.matchers = matchers;
specialStartSymbols.Clear();
specialEndSymbols.Clear();
if (delimiters != null)
{
foreach (char character in delimiters)
{
if (!specialStartSymbols.Contains(character))
{
specialStartSymbols.Add(character);
}
if (!specialEndSymbols.Contains(character))
{
specialEndSymbols.Add(character);
}
}
}
if (matchers != null)
{
foreach (Matcher lexer in matchers)
{
foreach (char special in lexer.StartChars)
{
if (!specialStartSymbols.Contains(special))
{
specialStartSymbols.Add(special);
}
}
foreach (char special in lexer.EndChars)
{
if (!specialEndSymbols.Contains(special))
{
specialEndSymbols.Add(special);
}
}
}
}
}
public IEnumerable<LexerMatchInfo> LexInputString(string input)
{
if (input == null || matchers == null || matchers.Length == 0)
{
yield break;
}
inputString = input;
current = ' ';
Previous = ' ';
currentIndex = 0;
currentLookaheadIndex = 0;
while (!EndOfStream)
{
bool didMatchLexer = false;
ReadWhiteSpace();
foreach (Matcher matcher in matchers)
{
int startIndex = currentIndex;
bool isMatched = matcher.IsMatch(this);
if (isMatched)
{
int endIndex = currentIndex;
didMatchLexer = true;
yield return new LexerMatchInfo
{
startIndex = startIndex,
endIndex = endIndex,
htmlColor = matcher.HexColor,
};
break;
}
}
if (!didMatchLexer)
{
ReadNext();
Commit();
}
}
}
public char ReadNext()
{
if (EndOfStream)
{
return '\0';
}
Previous = current;
current = inputString[currentLookaheadIndex];
currentLookaheadIndex++;
return current;
}
public void Rollback(int amount = -1)
{
if (amount == -1)
{
currentLookaheadIndex = currentIndex;
}
else
{
if (currentLookaheadIndex > currentIndex)
{
currentLookaheadIndex -= amount;
}
}
int previousIndex = currentLookaheadIndex - 1;
if (previousIndex >= inputString.Length)
{
Previous = inputString[inputString.Length - 1];
}
else if (previousIndex >= 0)
{
Previous = inputString[previousIndex];
}
else
{
Previous = ' ';
}
}
public void Commit()
{
currentIndex = currentLookaheadIndex;
}
public bool IsSpecialSymbol(char character, SpecialCharacterPosition position = SpecialCharacterPosition.Start)
{
if (position == SpecialCharacterPosition.Start)
{
return specialStartSymbols.Contains(character);
}
return specialEndSymbols.Contains(character);
}
private void ReadWhiteSpace()
{
while (char.IsWhiteSpace(ReadNext()) == true)
{
Commit();
}
Rollback();
}
}
}

View File

@ -3,23 +3,22 @@ using UnityEngine;
namespace UnityExplorer.Console.Lexer namespace UnityExplorer.Console.Lexer
{ {
public sealed class KeywordMatch : Matcher // I use two different KeywordMatch instances (valid and invalid).
// This class just contains common implementations.
public class KeywordMatch : Matcher
{ {
public string keywords; public string[] Keywords;
public override Color HighlightColor => highlightColor; public override Color HighlightColor => highlightColor;
public Color highlightColor; public Color highlightColor;
private readonly HashSet<string> shortlist = new HashSet<string>(); private readonly HashSet<string> shortlist = new HashSet<string>();
private readonly Stack<string> removeList = new Stack<string>(); private readonly Stack<string> removeList = new Stack<string>();
public string[] keywordCache = null;
public override bool IsImplicitMatch(InputLexer lexer) public override bool IsImplicitMatch(CSharpLexer lexer)
{ {
BuildKeywordCache();
if (!char.IsWhiteSpace(lexer.Previous) && if (!char.IsWhiteSpace(lexer.Previous) &&
!lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End)) !lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
{ {
return false; return false;
} }
@ -29,11 +28,11 @@ namespace UnityExplorer.Console.Lexer
int currentIndex = 0; int currentIndex = 0;
char currentChar = lexer.ReadNext(); char currentChar = lexer.ReadNext();
for (int i = 0; i < keywordCache.Length; i++) for (int i = 0; i < Keywords.Length; i++)
{ {
if (keywordCache[i][0] == currentChar) if (Keywords[i][0] == currentChar)
{ {
shortlist.Add(keywordCache[i]); shortlist.Add(Keywords[i]);
} }
} }
@ -54,7 +53,7 @@ namespace UnityExplorer.Console.Lexer
currentIndex++; currentIndex++;
if (char.IsWhiteSpace(currentChar) || if (char.IsWhiteSpace(currentChar) ||
lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start)) lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
{ {
RemoveLongStrings(currentIndex); RemoveLongStrings(currentIndex);
lexer.Rollback(1); lexer.Rollback(1);
@ -94,23 +93,5 @@ namespace UnityExplorer.Console.Lexer
shortlist.Remove(removeList.Pop()); shortlist.Remove(removeList.Pop());
} }
} }
private void BuildKeywordCache()
{
if (keywordCache == null)
{
string[] kwSplit = keywords.Split(' ');
List<string> list = new List<string>();
foreach (string kw in kwSplit)
{
if (!string.IsNullOrEmpty(kw) && kw.Length > 0)
{
list.Add(kw);
}
}
keywordCache = list.ToArray();
}
}
} }
} }

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityExplorer.Unstrip; using UnityExplorer.Unstrip;
using UnityEngine; using UnityEngine;
using System.Linq;
namespace UnityExplorer.Console.Lexer namespace UnityExplorer.Console.Lexer
{ {
@ -9,14 +10,14 @@ namespace UnityExplorer.Console.Lexer
public abstract Color HighlightColor { get; } public abstract Color HighlightColor { get; }
public string HexColor => htmlColor ?? (htmlColor = "<color=#" + HighlightColor.ToHex() + ">"); public string HexColor => htmlColor ?? (htmlColor = "<color=#" + HighlightColor.ToHex() + ">");
private string htmlColor = null; private string htmlColor;
public virtual IEnumerable<char> StartChars { get { yield break; } } public virtual IEnumerable<char> StartChars => Enumerable.Empty<char>();
public virtual IEnumerable<char> EndChars { get { yield break; } } public virtual IEnumerable<char> EndChars => Enumerable.Empty<char>();
public abstract bool IsImplicitMatch(InputLexer lexer); public abstract bool IsImplicitMatch(CSharpLexer lexer);
public bool IsMatch(InputLexer lexer) public bool IsMatch(CSharpLexer lexer)
{ {
if (IsImplicitMatch(lexer)) if (IsImplicitMatch(lexer))
{ {

View File

@ -2,14 +2,14 @@
namespace UnityExplorer.Console.Lexer namespace UnityExplorer.Console.Lexer
{ {
public sealed class NumberMatch : Matcher public class NumberMatch : Matcher
{ {
public override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f); public override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f);
public override bool IsImplicitMatch(InputLexer lexer) public override bool IsImplicitMatch(CSharpLexer lexer)
{ {
if (!char.IsWhiteSpace(lexer.Previous) && if (!char.IsWhiteSpace(lexer.Previous) &&
!lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End)) !lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
{ {
return false; return false;
} }

View File

@ -3,35 +3,24 @@ using UnityEngine;
namespace UnityExplorer.Console.Lexer namespace UnityExplorer.Console.Lexer
{ {
public sealed class StringMatch : Matcher public class StringMatch : Matcher
{ {
public override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f); public override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f);
public override IEnumerable<char> StartChars { get { yield return '"'; } } public override IEnumerable<char> StartChars => new[] { '"' };
public override IEnumerable<char> EndChars { get { yield return '"'; } } public override IEnumerable<char> EndChars => new[] { '"' };
public override bool IsImplicitMatch(InputLexer lexer) public override bool IsImplicitMatch(CSharpLexer lexer)
{ {
if (lexer.ReadNext() == '"') if (lexer.ReadNext() == '"')
{ {
while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) { }
{
;
}
return true; return true;
} }
return false; return false;
} }
private bool IsClosingQuoteOrEndFile(InputLexer lexer, char character) private bool IsClosingQuoteOrEndFile(CSharpLexer lexer, char character) => lexer.EndOfStream || character == '"';
{
if (lexer.EndOfStream == true ||
character == '"')
{
return true;
}
return false;
}
} }
} }

View File

@ -4,54 +4,32 @@ using UnityEngine;
namespace UnityExplorer.Console.Lexer namespace UnityExplorer.Console.Lexer
{ {
public sealed class SymbolMatch : Matcher public class SymbolMatch : Matcher
{ {
public override Color HighlightColor => new Color(0.58f, 0.47f, 0.37f, 1.0f); public override Color HighlightColor => new Color(0.58f, 0.47f, 0.37f, 1.0f);
public string Symbols => @"[ ] ( ) . ? : + - * / % & | ^ ~ = < > ++ -- && || << >> == != <= >= private readonly string[] symbols = new[]
+= -= *= /= %= &= |= ^= <<= >>= -> ?? =>"; {
"[", "]", "(", ")", ".", "?", ":", "+", "-", "*", "/", "%", "&", "|", "^", "~", "=", "<", ">",
"++", "--", "&&", "||", "<<", ">>", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "&=",
"|=", "^=", "<<=", ">>=", "->", "??", "=>",
};
private static readonly List<string> shortlist = new List<string>(); private static readonly List<string> shortlist = new List<string>();
private static readonly Stack<string> removeList = new Stack<string>(); private static readonly Stack<string> removeList = new Stack<string>();
private string[] symbolCache = null;
public override IEnumerable<char> StartChars public override IEnumerable<char> StartChars => symbols.Select(s => s[0]);
{ public override IEnumerable<char> EndChars => symbols.Select(s => s[0]);
get
{
BuildSymbolCache();
foreach (string symbol in symbolCache.Where(x => x.Length > 0))
{
yield return symbol[0];
}
}
}
public override IEnumerable<char> EndChars public override bool IsImplicitMatch(CSharpLexer lexer)
{
get
{
BuildSymbolCache();
foreach (string symbol in symbolCache.Where(x => x.Length > 0))
{
yield return symbol[0];
}
}
}
public override bool IsImplicitMatch(InputLexer lexer)
{ {
if (lexer == null) if (lexer == null)
{
return false; return false;
}
BuildSymbolCache();
if (!char.IsWhiteSpace(lexer.Previous) && if (!char.IsWhiteSpace(lexer.Previous) &&
!char.IsLetter(lexer.Previous) && !char.IsLetter(lexer.Previous) &&
!char.IsDigit(lexer.Previous) && !char.IsDigit(lexer.Previous) &&
!lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End)) !lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
{ {
return false; return false;
} }
@ -61,18 +39,14 @@ namespace UnityExplorer.Console.Lexer
int currentIndex = 0; int currentIndex = 0;
char currentChar = lexer.ReadNext(); char currentChar = lexer.ReadNext();
for (int i = symbolCache.Length - 1; i >= 0; i--) for (int i = symbols.Length - 1; i >= 0; i--)
{ {
if (symbolCache[i][0] == currentChar) if (symbols[i][0] == currentChar)
{ shortlist.Add(symbols[i]);
shortlist.Add(symbolCache[i]);
}
} }
if (shortlist.Count == 0) if (shortlist.Count == 0)
{
return false; return false;
}
do do
{ {
@ -88,7 +62,7 @@ namespace UnityExplorer.Console.Lexer
if (char.IsWhiteSpace(currentChar) || if (char.IsWhiteSpace(currentChar) ||
char.IsLetter(currentChar) || char.IsLetter(currentChar) ||
char.IsDigit(currentChar) || char.IsDigit(currentChar) ||
lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start)) lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
{ {
RemoveLongStrings(currentIndex); RemoveLongStrings(currentIndex);
lexer.Rollback(1); lexer.Rollback(1);
@ -128,24 +102,5 @@ namespace UnityExplorer.Console.Lexer
shortlist.Remove(removeList.Pop()); shortlist.Remove(removeList.Pop());
} }
} }
private void BuildSymbolCache()
{
if (symbolCache != null)
{
return;
}
string[] symSplit = Symbols.Split(' ');
List<string> list = new List<string>();
foreach (string sym in symSplit)
{
if (!string.IsNullOrEmpty(sym) && sym.Length > 0)
{
list.Add(sym);
}
}
symbolCache = list.ToArray();
}
} }
} }

View File

@ -9,25 +9,22 @@ namespace UnityExplorer.Console
{ {
public struct Suggestion public struct Suggestion
{ {
public string Full => Prefix + Addition; public enum Contexts
{
Namespace,
Keyword,
Other
}
// ~~~~ Instance ~~~~
public readonly string Prefix; public readonly string Prefix;
public readonly string Addition; public readonly string Addition;
public readonly Contexts Context; public readonly Contexts Context;
public Color TextColor public string Full => Prefix + Addition;
{
get public Color TextColor => GetTextColor();
{
switch (Context)
{
case Contexts.Namespace: return Color.grey;
case Contexts.Keyword: return systemBlue;
default: return Color.white;
}
}
}
private static readonly Color systemBlue = new Color(80f / 255f, 150f / 255f, 215f / 255f);
public Suggestion(string addition, string prefix, Contexts type) public Suggestion(string addition, string prefix, Contexts type)
{ {
@ -36,16 +33,26 @@ namespace UnityExplorer.Console
Context = type; Context = type;
} }
public enum Contexts private Color GetTextColor()
{ {
Namespace, switch (Context)
Keyword, {
Other case Contexts.Namespace: return Color.grey;
case Contexts.Keyword: return keywordColor;
default: return Color.white;
} }
}
// ~~~~ Static ~~~~
public static HashSet<string> Namespaces => m_namspaces ?? GetNamespaces(); public static HashSet<string> Namespaces => m_namspaces ?? GetNamespaces();
private static HashSet<string> m_namspaces; private static HashSet<string> m_namspaces;
public static HashSet<string> Keywords => m_keywords ?? (m_keywords = new HashSet<string>(CSharpLexer.validKeywordMatcher.Keywords));
private static HashSet<string> m_keywords;
private static readonly Color keywordColor = new Color(80f / 255f, 150f / 255f, 215f / 255f);
private static HashSet<string> GetNamespaces() private static HashSet<string> GetNamespaces()
{ {
HashSet<string> set = new HashSet<string>( HashSet<string> set = new HashSet<string>(
@ -58,25 +65,5 @@ namespace UnityExplorer.Console
IEnumerable<Type> GetTypes(Assembly asm) => asm.TryGetTypes(); IEnumerable<Type> GetTypes(Assembly asm) => asm.TryGetTypes();
} }
public static HashSet<string> Keywords => m_keywords ?? GetKeywords();
private static HashSet<string> m_keywords;
private static HashSet<string> GetKeywords()
{
if (CSharpLexer.validKeywordMatcher.keywordCache == null)
{
return new HashSet<string>();
}
HashSet<string> set = new HashSet<string>();
foreach (string keyword in CSharpLexer.validKeywordMatcher.keywordCache)
{
set.Add(keyword);
}
return m_keywords = set;
}
} }
} }

View File

@ -78,18 +78,15 @@ namespace UnityExplorer.UI
if (selectable is Button button) if (selectable is Button button)
{ {
#if CPP #if CPP
button.onClick.AddListener(new Action(() => button.onClick.AddListener(new Action(Deselect));
{
button.OnDeselect(null);
}));
#else #else
button.onClick.AddListener(Deselect); button.onClick.AddListener(Deselect);
#endif
void Deselect() void Deselect()
{ {
button.OnDeselect(null); button.OnDeselect(null);
} }
#endif
} }
selectable.colors = colors; selectable.colors = colors;

View File

@ -355,9 +355,8 @@
<Compile Include="UI\PageModel\ConsolePage.cs" /> <Compile Include="UI\PageModel\ConsolePage.cs" />
<Compile Include="Console\AutoCompleter.cs" /> <Compile Include="Console\AutoCompleter.cs" />
<Compile Include="Console\CodeEditor.cs" /> <Compile Include="Console\CodeEditor.cs" />
<Compile Include="Console\CSharpLexer.cs" />
<Compile Include="Console\Lexer\CommentMatch.cs" /> <Compile Include="Console\Lexer\CommentMatch.cs" />
<Compile Include="Console\Lexer\InputLexer.cs" /> <Compile Include="Console\CSharpLexer.cs" />
<Compile Include="Console\Lexer\KeywordMatch.cs" /> <Compile Include="Console\Lexer\KeywordMatch.cs" />
<Compile Include="Console\Lexer\StringMatch.cs" /> <Compile Include="Console\Lexer\StringMatch.cs" />
<Compile Include="Console\Lexer\Matcher.cs" /> <Compile Include="Console\Lexer\Matcher.cs" />