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 textGen = editor.inputText.cachedTextGenerator;
var textGen = editor.InputText.cachedTextGenerator;
//if (textGen.characters.Count < 1)
// return;

View File

@ -1,19 +1,45 @@
using System.Collections.Generic;
using System.Text;
using UnityExplorer.Console.Lexer;
using UnityEngine;
using UnityExplorer.Console.Lexer;
namespace UnityExplorer.Console
{
public static class CSharpLexer
public struct LexerMatchInfo
{
public static char indentIncreaseCharacter = '{';
public static char indentDecreaseCharacter = '}';
public int startIndex;
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();
public static char[] delimiters = new[]
{
'[', ']', '(', ')', '{', '}', ';', ':', ',', '.'
};
public static CommentMatch commentMatcher = new CommentMatch();
public static SymbolMatch symbolMatcher = new SymbolMatch();
public static NumberMatch numberMatcher = new NumberMatch();
@ -22,80 +48,109 @@ namespace UnityExplorer.Console
public static KeywordMatch validKeywordMatcher = new KeywordMatch
{
highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f),
keywords = @"add as ascending await bool break by byte
case catch char checked const continue decimal default descending do dynamic
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
ref remove return sbyte select short sizeof stackalloc string
switch throw true try typeof uint ulong ushort
var where while yield"
Keywords = new[] { "add", "as", "ascending", "await", "bool", "break", "by", "byte",
"case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic",
"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",
"ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string",
"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield" }
};
public static KeywordMatch invalidKeywordMatcher = new KeywordMatch()
{
highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f),
keywords = @"abstract async base class delegate enum explicit extern fixed get
implicit interface internal namespace operator override params private protected public
using partial readonly sealed set static struct this unchecked unsafe value virtual volatile void"
Keywords = new[] { "abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" }
};
private static char[] delimiterSymbolCache = null;
internal static char[] DelimiterSymbols
// ~~~~~~~ ctor ~~~~~~~
public CSharpLexer()
{
get
startDelimiters = new HashSet<char>(delimiters);
endDelimiters = new HashSet<char>(delimiters);
this.matchers = new Matcher[]
{
if (delimiterSymbolCache == null)
commentMatcher,
symbolMatcher,
numberMatcher,
stringMatcher,
validKeywordMatcher,
invalidKeywordMatcher,
};
foreach (Matcher lexer in matchers)
{
foreach (char c in lexer.StartChars)
{
string[] symbols = delimiterSymbols.Split(' ');
int count = 0;
for (int i = 0; i < symbols.Length; i++)
{
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++;
}
}
if (!startDelimiters.Contains(c))
startDelimiters.Add(c);
}
foreach (char c in lexer.EndChars)
{
if (!endDelimiters.Contains(c))
endDelimiters.Add(c);
}
return delimiterSymbolCache;
}
}
private static Matcher[] matchers = null;
internal static Matcher[] Matchers
{
get
{
if (matchers == null)
{
List<Matcher> matcherList = new List<Matcher>
{
commentMatcher,
symbolMatcher,
numberMatcher,
stringMatcher,
validKeywordMatcher,
invalidKeywordMatcher,
};
// ~~~~~~~ Lex Matching ~~~~~~~
matchers = matcherList.ToArray();
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();
}
return matchers;
}
}
// ~~~~~~~ Indent ~~~~~~~
public static string GetIndentForInput(string input, int indent, out int caretPosition)
{
indentBuilder.Clear();
@ -123,14 +178,14 @@ namespace UnityExplorer.Console
{
continue;
}
else if (!stringState && input[i] == indentIncreaseCharacter)
else if (!stringState && input[i] == indentOpen)
{
indentBuilder.Append(indentIncreaseCharacter);
indentBuilder.Append(indentOpen);
indent++;
}
else if (!stringState && input[i] == indentDecreaseCharacter)
else if (!stringState && input[i] == indentClose)
{
indentBuilder.Append(indentDecreaseCharacter);
indentBuilder.Append(indentClose);
indent--;
}
else
@ -177,5 +232,77 @@ namespace UnityExplorer.Console
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
{
// Handles most of the UI side of the C# console, including syntax highlighting.
public class CodeEditor
{
private readonly InputLexer inputLexer = new InputLexer();
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 Image background;
private Image scrollbar;
public int LineCount { get; private set; }
public int CurrentLine { get; private set; }
public int CurrentIndent { get; private set; }
private static readonly StringBuilder highlightedBuilder = new StringBuilder(4096);
public string HighlightedText => inputHighlightText.text;
private readonly CSharpLexer highlightLexer;
private readonly StringBuilder sbHighlight;
private static readonly KeyCode[] onFocusKeys =
{
@ -41,28 +40,6 @@ namespace UnityExplorer.Console
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.
The following helper methods are available:
@ -86,14 +63,14 @@ The following helper methods are available:
public CodeEditor()
{
ConstructUI();
sbHighlight = new StringBuilder();
highlightLexer = new CSharpLexer();
ApplyTheme();
inputLexer.UseMatchers(CSharpLexer.DelimiterSymbols, CSharpLexer.Matchers);
ConstructUI();
// subscribe to text input changing
#if CPP
InputField.onValueChanged.AddListener(new Action<string>((string s) => { OnInputChanged(); }));
InputField.onValueChanged.AddListener(new Action<string>((string s) => { OnInputChanged(s); }));
#else
this.InputField.onValueChanged.AddListener((string s) => { OnInputChanged(s); });
#endif
@ -166,10 +143,10 @@ The following helper methods are available:
{
char character = newText[i];
if (character == CSharpLexer.indentIncreaseCharacter)
if (character == CSharpLexer.indentOpen)
CurrentIndent++;
if (character == CSharpLexer.indentDecreaseCharacter)
if (character == CSharpLexer.indentClose)
CurrentIndent--;
}
@ -183,33 +160,33 @@ The following helper methods are available:
{
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++)
{
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++)
{
highlightedBuilder.Append(inputText[i]);
sbHighlight.Append(inputText[i]);
}
highlightedBuilder.Append(CLOSE_COLOR_TAG);
sbHighlight.Append(CLOSE_COLOR_TAG);
offset = match.endIndex;
}
for (int i = offset; i < inputText.Length; i++)
{
highlightedBuilder.Append(inputText[i]);
sbHighlight.Append(inputText[i]);
}
inputText = highlightedBuilder.ToString();
inputText = sbHighlight.ToString();
return inputText;
}
@ -242,8 +219,8 @@ The following helper methods are available:
}
// check if should add auto-close }
int numOpen = InputField.text.Where(x => x == CSharpLexer.indentIncreaseCharacter).Count();
int numClose = InputField.text.Where(x => x == CSharpLexer.indentDecreaseCharacter).Count();
int numOpen = InputField.text.Where(x => x == CSharpLexer.indentOpen).Count();
int numClose = InputField.text.Where(x => x == CSharpLexer.indentClose).Count();
if (numOpen > numClose)
{
@ -263,13 +240,13 @@ The following helper methods are available:
// Update line column and indent positions
UpdateIndent(InputField.text);
inputText.text = InputField.text;
InputText.text = InputField.text;
//inputText.SetText(InputField.text, true);
inputText.Rebuild(CanvasUpdate.Prelayout);
InputText.Rebuild(CanvasUpdate.Prelayout);
InputField.ForceLabelUpdate();
InputField.Rebuild(CanvasUpdate.Prelayout);
OnInputChanged(inputText.text, true);
OnInputChanged(InputText.text, true);
}
private string GetAutoIndentTab(int amount)
@ -284,26 +261,6 @@ The following helper methods are available:
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 =========== //
public void ConstructUI()
@ -523,14 +480,27 @@ The following helper methods are available:
mainTextInput.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
this.InputField = inputField;
this.inputText = mainTextInput;
this.InputText = mainTextInput;
this.inputHighlightText = highlightTextInput;
this.background = mainBgImage;
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
{
public sealed class CommentMatch : Matcher
public class CommentMatch : Matcher
{
public string lineCommentStart = @"//";
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 IEnumerable<char> StartChars => new char[] { lineCommentStart[0], blockCommentStart[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))
{
@ -32,11 +32,8 @@ namespace UnityExplorer.Console.Lexer
if (match)
{
// Read until end
while (!IsEndLineOrEndFile(lexer, lexer.ReadNext()))
{
;
}
// Read until end of line or file
while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) { }
return true;
}
@ -44,6 +41,6 @@ namespace UnityExplorer.Console.Lexer
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
{
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 Color highlightColor;
private readonly HashSet<string> shortlist = new HashSet<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) &&
!lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End))
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
{
return false;
}
@ -29,11 +28,11 @@ namespace UnityExplorer.Console.Lexer
int currentIndex = 0;
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++;
if (char.IsWhiteSpace(currentChar) ||
lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start))
lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
{
RemoveLongStrings(currentIndex);
lexer.Rollback(1);
@ -94,23 +93,5 @@ namespace UnityExplorer.Console.Lexer
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 UnityExplorer.Unstrip;
using UnityEngine;
using System.Linq;
namespace UnityExplorer.Console.Lexer
{
@ -9,14 +10,14 @@ namespace UnityExplorer.Console.Lexer
public abstract Color HighlightColor { get; }
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> EndChars { get { yield break; } }
public virtual IEnumerable<char> StartChars => Enumerable.Empty<char>();
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))
{

View File

@ -2,14 +2,14 @@
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 bool IsImplicitMatch(InputLexer lexer)
public override bool IsImplicitMatch(CSharpLexer lexer)
{
if (!char.IsWhiteSpace(lexer.Previous) &&
!lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End))
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
{
return false;
}

View File

@ -3,35 +3,24 @@ using UnityEngine;
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 IEnumerable<char> StartChars { get { yield return '"'; } }
public override IEnumerable<char> EndChars { get { yield return '"'; } }
public override IEnumerable<char> StartChars => new[] { '"' };
public override IEnumerable<char> EndChars => new[] { '"' };
public override bool IsImplicitMatch(InputLexer lexer)
public override bool IsImplicitMatch(CSharpLexer lexer)
{
if (lexer.ReadNext() == '"')
{
while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext()))
{
;
}
while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) { }
return true;
}
return false;
}
private bool IsClosingQuoteOrEndFile(InputLexer lexer, char character)
{
if (lexer.EndOfStream == true ||
character == '"')
{
return true;
}
return false;
}
private bool IsClosingQuoteOrEndFile(CSharpLexer lexer, char character) => lexer.EndOfStream || character == '"';
}
}

View File

@ -4,54 +4,32 @@ using UnityEngine;
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 string Symbols => @"[ ] ( ) . ? : + - * / % & | ^ ~ = < > ++ -- && || << >> == != <= >=
+= -= *= /= %= &= |= ^= <<= >>= -> ?? =>";
private readonly string[] symbols = new[]
{
"[", "]", "(", ")", ".", "?", ":", "+", "-", "*", "/", "%", "&", "|", "^", "~", "=", "<", ">",
"++", "--", "&&", "||", "<<", ">>", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "&=",
"|=", "^=", "<<=", ">>=", "->", "??", "=>",
};
private static readonly List<string> shortlist = new List<string>();
private static readonly Stack<string> removeList = new Stack<string>();
private string[] symbolCache = null;
public override IEnumerable<char> StartChars
{
get
{
BuildSymbolCache();
foreach (string symbol in symbolCache.Where(x => x.Length > 0))
{
yield return symbol[0];
}
}
}
public override IEnumerable<char> StartChars => symbols.Select(s => s[0]);
public override IEnumerable<char> EndChars => symbols.Select(s => s[0]);
public override IEnumerable<char> EndChars
{
get
{
BuildSymbolCache();
foreach (string symbol in symbolCache.Where(x => x.Length > 0))
{
yield return symbol[0];
}
}
}
public override bool IsImplicitMatch(InputLexer lexer)
public override bool IsImplicitMatch(CSharpLexer lexer)
{
if (lexer == null)
{
return false;
}
BuildSymbolCache();
if (!char.IsWhiteSpace(lexer.Previous) &&
!char.IsLetter(lexer.Previous) &&
!char.IsDigit(lexer.Previous) &&
!lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End))
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
{
return false;
}
@ -61,18 +39,14 @@ namespace UnityExplorer.Console.Lexer
int currentIndex = 0;
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)
{
shortlist.Add(symbolCache[i]);
}
if (symbols[i][0] == currentChar)
shortlist.Add(symbols[i]);
}
if (shortlist.Count == 0)
{
return false;
}
do
{
@ -88,7 +62,7 @@ namespace UnityExplorer.Console.Lexer
if (char.IsWhiteSpace(currentChar) ||
char.IsLetter(currentChar) ||
char.IsDigit(currentChar) ||
lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start))
lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
{
RemoveLongStrings(currentIndex);
lexer.Rollback(1);
@ -128,24 +102,5 @@ namespace UnityExplorer.Console.Lexer
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 string Full => Prefix + Addition;
public enum Contexts
{
Namespace,
Keyword,
Other
}
// ~~~~ Instance ~~~~
public readonly string Prefix;
public readonly string Addition;
public readonly Contexts Context;
public Color TextColor
{
get
{
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 string Full => Prefix + Addition;
public Color TextColor => GetTextColor();
public Suggestion(string addition, string prefix, Contexts type)
{
@ -36,16 +33,26 @@ namespace UnityExplorer.Console
Context = type;
}
public enum Contexts
private Color GetTextColor()
{
Namespace,
Keyword,
Other
switch (Context)
{
case Contexts.Namespace: return Color.grey;
case Contexts.Keyword: return keywordColor;
default: return Color.white;
}
}
// ~~~~ Static ~~~~
public static HashSet<string> Namespaces => m_namspaces ?? GetNamespaces();
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()
{
HashSet<string> set = new HashSet<string>(
@ -58,25 +65,5 @@ namespace UnityExplorer.Console
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 CPP
button.onClick.AddListener(new Action(() =>
{
button.OnDeselect(null);
}));
button.onClick.AddListener(new Action(Deselect));
#else
button.onClick.AddListener(Deselect);
#endif
void Deselect()
{
button.OnDeselect(null);
}
#endif
}
selectable.colors = colors;

View File

@ -355,9 +355,8 @@
<Compile Include="UI\PageModel\ConsolePage.cs" />
<Compile Include="Console\AutoCompleter.cs" />
<Compile Include="Console\CodeEditor.cs" />
<Compile Include="Console\CSharpLexer.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\StringMatch.cs" />
<Compile Include="Console\Lexer\Matcher.cs" />