mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-01-09 18:48:46 +08:00
cleanup and refactor C# lexer classes
This commit is contained in:
parent
9c077b3aa3
commit
70a1570441
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 == '"';
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user