diff --git a/src/Explorer.csproj b/src/Explorer.csproj
index 173ba96..1f9a6e3 100644
--- a/src/Explorer.csproj
+++ b/src/Explorer.csproj
@@ -256,18 +256,16 @@
-
-
-
-
+
+
-
-
-
+
+
+
-
-
+
+
diff --git a/src/UI/Main/Pages/Console/Editor/AutoIndent.cs b/src/UI/Main/Pages/Console/Editor/AutoIndent.cs
deleted file mode 100644
index dd66ddb..0000000
--- a/src/UI/Main/Pages/Console/Editor/AutoIndent.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Explorer.UI.Main.Pages.Console
-{
- public class AutoIndent
- {
- // Enum
- public enum IndentMode
- {
- None,
- AutoTab,
- AutoTabContextual,
- }
-
- // Private
- private static readonly StringBuilder indentBuilder = new StringBuilder();
-
- private static string indentDecreaseString = null;
-
- // Public
- public static IndentMode autoIndentMode = IndentMode.AutoTabContextual;
-
- ///
- /// Should auto indent be used for this language.
- ///
- public static bool allowAutoIndent = true;
- ///
- /// The character that causes the indent level to increase.
- ///
- public static char indentIncreaseCharacter = '{';
- ///
- /// The character that causes the indent level to decrease.
- ///
- public static char indentDecreaseCharacter = '}';
-
- // Properties
- ///
- /// Get the string representation of the indent character.
- ///
- public static string IndentDecreaseString
- {
- get
- {
- if (indentDecreaseString == null)
- {
- indentDecreaseString = new string(indentDecreaseCharacter, 1);
- }
- return indentDecreaseString;
- }
- }
-
- // Methods
- public static string GetAutoIndentedFormattedString(string indentSection, int currentIndent, out int caretPosition)
- {
- // Add indent level
- int indent = currentIndent + 1;
-
- // Append characters
- for (int i = 0; i < indentSection.Length; i++)
- {
- if (indentSection[i] == '\n')
- {
- indentBuilder.Append('\n');
- AppendIndentString(indent);
- }
- else if (indentSection[i] == '\t')
- {
- // We will add tabs manually
- continue;
- }
- else if (indentSection[i] == indentIncreaseCharacter)
- {
- indentBuilder.Append(indentIncreaseCharacter);
- indent++;
- }
- else if (indentSection[i] == indentDecreaseCharacter)
- {
- indentBuilder.Append(indentDecreaseCharacter);
- indent--;
- }
- else
- {
- indentBuilder.Append(indentSection[i]);
- }
- }
-
- // Build the string
- string formattedSection = indentBuilder.ToString();
- indentBuilder.Length = 0;
-
- // Default caret position
- caretPosition = formattedSection.Length - 1;
-
- // Find the caret position
- for (int i = formattedSection.Length - 1; i >= 0; i--)
- {
- if (formattedSection[i] == '\n')
- continue;
-
- caretPosition = i;
- break;
- }
-
- return formattedSection;
- }
-
- public static int GetAutoIndentLevel(string inputString, int startIndex, int endIndex)
- {
- int indent = 0;
-
- for (int i = startIndex; i < endIndex; i++)
- {
- if (inputString[i] == '\t')
- indent++;
-
- // Check for end line or other characters
- if (inputString[i] == '\n' || inputString[i] != ' ')
- break;
- }
-
- return indent;
- }
-
- private static void AppendIndentString(int amount)
- {
- for (int i = 0; i < amount; i++)
- indentBuilder.Append("\t");
- }
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/CSharpLexer.cs b/src/UI/Main/Pages/Console/Editor/CSharpLexer.cs
new file mode 100644
index 0000000..ebda1d8
--- /dev/null
+++ b/src/UI/Main/Pages/Console/Editor/CSharpLexer.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+using Explorer.UI.Main.Pages.Console.Lexer;
+using System.Runtime.InteropServices;
+
+namespace Explorer.UI.Main.Pages.Console
+{
+ public static class CSharpLexer
+ {
+ // todo option to disable auto-indent
+ public static bool allowAutoIndent = true;
+ public static char indentIncreaseCharacter = '{';
+ public static char indentDecreaseCharacter = '}';
+
+ public static string delimiterSymbols = "[ ] ( ) { } ; : , .";
+
+ private static readonly StringBuilder indentBuilder = new StringBuilder();
+
+ public static CommentMatch commentMatcher = new CommentMatch();
+ public static SymbolMatch symbolMatcher = new SymbolMatch();
+ public static NumberMatch numberMatcher = new NumberMatch();
+ public static StringMatch stringMatcher = new StringMatch();
+
+ public static KeywordMatch validKeywordMatcher = new KeywordMatch
+ {
+ highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f),
+ keywords = @"add as ascending await base bool break by byte
+ case catch char checked const continue decimal default descending do dynamic
+ else enum equals false finally fixed float for foreach from global goto group
+ if in int into is join let lock long new null object on orderby out params
+ ref remove return sbyte select short sizeof stackalloc string
+ struct switch this throw true try typeof uint ulong unchecked unsafe ushort
+ value var where while yield"
+ };
+
+ public static KeywordMatch invalidKeywordMatcher = new KeywordMatch()
+ {
+ highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f),
+ keywords = @"abstract async class delegate explicit extern get
+ implicit interface internal namespace operator override private protected public
+ using partial readonly sealed set static virtual volatile void"
+ };
+
+ private static char[] delimiterSymbolCache = null;
+ internal static char[] DelimiterSymbols
+ {
+ get
+ {
+ if (delimiterSymbolCache == null)
+ {
+ 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++;
+ }
+ }
+ }
+ return delimiterSymbolCache;
+ }
+ }
+
+ private static MatchLexer[] matchers = null;
+ internal static MatchLexer[] Matchers
+ {
+ get
+ {
+ if (matchers == null)
+ {
+ List matcherList = new List
+ {
+ commentMatcher,
+ symbolMatcher,
+ numberMatcher,
+ stringMatcher,
+ validKeywordMatcher,
+ invalidKeywordMatcher,
+ };
+
+ matchers = matcherList.ToArray();
+ }
+ return matchers;
+ }
+ }
+
+ public static string GetIndentForInput(string input, int indent, out int caretPosition)
+ {
+ indentBuilder.Clear();
+
+ indent += 1;
+
+ bool stringState = false;
+
+ for (int i = 0; i < input.Length; i++)
+ {
+ if (input[i] == '"')
+ {
+ stringState = !stringState;
+ }
+
+ if (input[i] == '\n')
+ {
+ indentBuilder.Append('\n');
+ for (int j = 0; j < indent; j++)
+ indentBuilder.Append("\t");
+ }
+ else if (input[i] == '\t')
+ {
+ continue;
+ }
+ else if (!stringState && input[i] == indentIncreaseCharacter)
+ {
+ indentBuilder.Append(indentIncreaseCharacter);
+ indent++;
+ }
+ else if (!stringState && input[i] == indentDecreaseCharacter)
+ {
+ indentBuilder.Append(indentDecreaseCharacter);
+ indent--;
+ }
+ else
+ {
+ indentBuilder.Append(input[i]);
+ }
+ }
+
+ string formattedSection = indentBuilder.ToString();
+
+ caretPosition = formattedSection.Length - 1;
+
+ for (int i = formattedSection.Length - 1; i >= 0; i--)
+ {
+ if (formattedSection[i] == '\n')
+ continue;
+
+ caretPosition = i;
+ break;
+ }
+
+ return formattedSection;
+ }
+
+ public static int GetIndentLevel(string inputString, int startIndex, int endIndex)
+ {
+ int indent = 0;
+
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ if (inputString[i] == '\t')
+ indent++;
+
+ // Check for end line or other characters
+ if (inputString[i] == '\n' || inputString[i] != ' ')
+ break;
+ }
+
+ return indent;
+ }
+ }
+}
diff --git a/src/UI/Main/Pages/Console/Editor/CodeEditor.cs b/src/UI/Main/Pages/Console/Editor/CodeEditor.cs
index 828cf7e..2b54379 100644
--- a/src/UI/Main/Pages/Console/Editor/CodeEditor.cs
+++ b/src/UI/Main/Pages/Console/Editor/CodeEditor.cs
@@ -7,60 +7,16 @@ using System.Reflection;
using ExplorerBeta.Input;
using Explorer.UI.Main.Pages.Console.Lexer;
using ExplorerBeta;
+using System.Linq;
namespace Explorer.UI.Main.Pages.Console
{
public class CodeEditor
- {
- public CodeEditor(TMP_InputField inputField, TextMeshProUGUI inputText, TextMeshProUGUI inputHighlightText, TextMeshProUGUI lineText,
- Image background, Image lineHighlight, Image lineNumberBackground, Image scrollbar)
- {
- this.InputField = inputField;
- this.inputText = inputText;
- this.inputHighlightText = inputHighlightText;
- this.lineText = lineText;
- this.background = background;
- this.lineHighlight = lineHighlight;
- this.lineNumberBackground = lineNumberBackground;
- this.scrollbar = scrollbar;
+ {
+ private readonly InputLexer inputLexer = new InputLexer();
- var highlightTextRect = inputHighlightText.GetComponent();
- highlightTextRect.anchorMin = Vector2.zero;
- highlightTextRect.anchorMax = Vector2.one;
- highlightTextRect.offsetMin = Vector2.zero;
- highlightTextRect.offsetMax = Vector2.zero;
+ public TMP_InputField InputField { get; }
- if (!AllReferencesAssigned())
- {
- throw new Exception("CodeEditor: Components are missing!");
- }
-
- this.inputTextTransform = inputText.GetComponent();
- this.lineHighlightTransform = lineHighlight.GetComponent();
-
- ApplyTheme();
- ApplyLanguage();
-
- // subscribe to text input changing
- InputField.onValueChanged.AddListener(new Action((string s) => { Refresh(); }));
- }
-
- private static readonly KeyCode[] lineChangeKeys =
- {
- KeyCode.Return, KeyCode.Backspace, KeyCode.UpArrow,
- KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow
- };
-
- private static readonly StringBuilder highlightedBuilder = new StringBuilder(4096);
- private static readonly StringBuilder lineBuilder = new StringBuilder();
-
- private readonly InputStringLexer lexer = new InputStringLexer();
- private readonly RectTransform inputTextTransform;
- private readonly RectTransform lineHighlightTransform;
- private string lastText;
- private bool lineHighlightLocked;
-
- public readonly TMP_InputField InputField;
private readonly TextMeshProUGUI inputText;
private readonly TextMeshProUGUI inputHighlightText;
private readonly TextMeshProUGUI lineText;
@@ -69,13 +25,26 @@ namespace Explorer.UI.Main.Pages.Console
private readonly Image lineNumberBackground;
private readonly Image scrollbar;
- private bool lineNumbers = true;
- private int lineNumbersSize = 20;
+ private string lastText;
+ private bool lineHighlightLocked;
+ private readonly RectTransform inputTextTransform;
+ private readonly RectTransform lineHighlightTransform;
- public int LineCount { get; private set; } = 0;
- public int CurrentLine { get; private set; } = 0;
- public int CurrentColumn { get; private set; } = 0;
- public int CurrentIndent { get; private set; } = 0;
+ public int LineCount { get; private set; }
+ public int CurrentLine { get; private set; }
+ public int CurrentColumn { get; private set; }
+ public int CurrentIndent { get; private set; }
+
+ private static readonly StringBuilder highlightedBuilder = new StringBuilder(4096);
+ private static readonly StringBuilder lineBuilder = new StringBuilder();
+
+ private static readonly KeyCode[] lineChangeKeys =
+ {
+ KeyCode.Return, KeyCode.Backspace, KeyCode.UpArrow,
+ KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow
+ };
+
+ public string HighlightedText => inputHighlightText.text;
public string Text
{
@@ -97,64 +66,39 @@ namespace Explorer.UI.Main.Pages.Console
}
}
- public string HighlightedText => inputHighlightText.text;
-
- public bool LineNumbers
+ public CodeEditor(TMP_InputField inputField, TextMeshProUGUI inputText, TextMeshProUGUI inputHighlightText, TextMeshProUGUI lineText,
+ Image background, Image lineHighlight, Image lineNumberBackground, Image scrollbar)
{
- get { return lineNumbers; }
- set
+ this.InputField = inputField;
+ this.inputText = inputText;
+ this.inputHighlightText = inputHighlightText;
+ this.lineText = lineText;
+ this.background = background;
+ this.lineHighlight = lineHighlight;
+ this.lineNumberBackground = lineNumberBackground;
+ this.scrollbar = scrollbar;
+
+ if (!AllReferencesAssigned())
{
- lineNumbers = value;
-
- //RectTransform inputFieldTransform = InputField.transform as RectTransform;
- //RectTransform lineNumberBackgroudTransform = lineNumberBackground.transform as RectTransform;
-
- //// Check for line numbers
- //if (lineNumbers == true)
- //{
- // // Enable line numbers
- // lineNumberBackground.gameObject.SetActive(true);
- // lineText.gameObject.SetActive(true);
-
- // // Set left value
- // inputFieldTransform.offsetMin = new Vector2(lineNumbersSize, inputFieldTransform.offsetMin.y);
- // lineNumberBackgroudTransform.sizeDelta = new Vector2(lineNumbersSize + 15, lineNumberBackgroudTransform.sizeDelta.y);
- //}
- //else
- //{
- // // Disable line numbers
- // lineNumberBackground.gameObject.SetActive(false);
- // lineText.gameObject.SetActive(false);
-
- // // Set left value
- // inputFieldTransform.offsetMin = new Vector2(0, inputFieldTransform.offsetMin.y);
- //}
+ throw new Exception("References are missing!");
}
- }
- // todo maybe not needed
- public int LineNumbersSize
- {
- get { return lineNumbersSize; }
- set
- {
- lineNumbersSize = value;
+ this.inputTextTransform = inputText.GetComponent();
+ this.lineHighlightTransform = lineHighlight.GetComponent();
- // Update the line numbers
- LineNumbers = lineNumbers;
- }
+ ApplyTheme();
+ inputLexer.UseMatchers(CSharpLexer.DelimiterSymbols, CSharpLexer.Matchers);
+
+ // subscribe to text input changing
+ this.InputField.onValueChanged.AddListener(new Action((string s) => { OnInputChanged(); }));
}
public void Update()
{
- // Auto indent
- if (AutoIndent.autoIndentMode != AutoIndent.IndentMode.None)
+ // Check for new line
+ if (InputManager.GetKeyDown(KeyCode.Return))
{
- // Check for new line
- if (InputManager.GetKeyDown(KeyCode.Return))
- {
- AutoIndentCaret();
- }
+ AutoIndentCaret();
}
bool focusKeyPressed = false;
@@ -172,54 +116,16 @@ namespace Explorer.UI.Main.Pages.Console
// Update line highlight
if (focusKeyPressed || InputManager.GetMouseButton(0))
{
- UpdateCurrentLineHighlight();
+ UpdateHighlight();
}
}
- public void Refresh(bool forceUpdate = false)
+ public void OnInputChanged(bool forceUpdate = false)
{
- // Trigger a content change event
- DisplayedContentChanged(InputField.text, forceUpdate);
- }
+ var newText = InputField.text;
- public void SetLineHighlight(int lineNumber, bool lockLineHighlight)
- {
- // Check if code editor is not active
- if (lineNumber < 1 || lineNumber > LineCount)
- return;
+ UpdateIndent();
- //int lineOffset = 0;
- //int lineIndex = lineNumber - 1;
-
- // Highlight the current line
- lineHighlightTransform.anchoredPosition = new Vector2(5,
- (inputText.textInfo.lineInfo[inputText.textInfo.characterInfo[0].lineNumber].lineHeight *
- -(lineNumber - 1)) - 4f +
- inputTextTransform.anchoredPosition.y);
-
- // Lock the line highlight so it cannot be moved
- if (lockLineHighlight == true)
- LockLineHighlight();
- else
- UnlockLineHighlight();
- }
-
- public void LockLineHighlight()
- {
- lineHighlightLocked = true;
- }
-
- public void UnlockLineHighlight()
- {
- lineHighlightLocked = false;
- }
-
- private void DisplayedContentChanged(string newText, bool forceUpdate)
- {
- // Update caret position
- UpdateCurrentLineColumnIndent();
-
- // Check for change
if ((!forceUpdate && lastText == newText) || string.IsNullOrEmpty(newText))
{
if (string.IsNullOrEmpty(newText))
@@ -227,37 +133,45 @@ namespace Explorer.UI.Main.Pages.Console
inputHighlightText.text = string.Empty;
}
- // Its possible the text was cleared so we need to sync numbers and highlighter
- UpdateCurrentLineNumbers();
- UpdateCurrentLineHighlight();
+ UpdateLineNumbers();
+ UpdateHighlight();
return;
}
inputHighlightText.text = SyntaxHighlightContent(newText);
// Sync line numbers and update the line highlight
- UpdateCurrentLineNumbers();
- UpdateCurrentLineHighlight();
+ UpdateLineNumbers();
+ UpdateHighlight();
this.lastText = newText;
}
- private void UpdateCurrentLineNumbers()
+ public void SetLineHighlight(int lineNumber, bool lockLineHighlight)
+ {
+ if (lineNumber < 1 || lineNumber > LineCount)
+ return;
+
+ lineHighlightTransform.anchoredPosition = new Vector2(5,
+ (inputText.textInfo.lineInfo[inputText.textInfo.characterInfo[0].lineNumber].lineHeight *
+ -(lineNumber - 1)) - 4f +
+ inputTextTransform.anchoredPosition.y);
+
+ lineHighlightLocked = lockLineHighlight;
+ }
+
+ private void UpdateLineNumbers()
{
- // Get the line count
int currentLineCount = inputText.textInfo.lineCount;
int currentLineNumber = 1;
- // Check for a change in line
if (currentLineCount != LineCount)
{
try
{
- // Update line numbers
lineBuilder.Length = 0;
- // Build line numbers string
for (int i = 1; i < currentLineCount + 2; i++)
{
if (i - 1 > 0 && i - 1 < currentLineCount - 1)
@@ -292,7 +206,6 @@ namespace Explorer.UI.Main.Pages.Console
}
}
- // Update displayed line numbers
lineText.text = lineBuilder.ToString();
LineCount = currentLineCount;
}
@@ -300,9 +213,8 @@ namespace Explorer.UI.Main.Pages.Console
}
}
- private void UpdateCurrentLineColumnIndent()
+ private void UpdateIndent()
{
- // Get the current line number
int caret = InputField.caretPosition;
if (caret < 0 || caret >= inputText.textInfo.characterInfo.Count)
@@ -318,48 +230,35 @@ namespace Explorer.UI.Main.Pages.Console
CurrentLine = inputText.textInfo.characterInfo[caret].lineNumber;
- // Get the total character count
int charCount = 0;
for (int i = 0; i < CurrentLine; i++)
charCount += inputText.textInfo.lineInfo[i].characterCount;
- // Get the column position
CurrentColumn = caret - charCount;
-
CurrentIndent = 0;
- // Check for auto indent allowed
- if (AutoIndent.allowAutoIndent)
+ for (int i = 0; i < caret && i < InputField.text.Length; i++)
{
- for (int i = 0; i < caret && i < InputField.text.Length; i++)
- {
- char character = InputField.text[i];
+ char character = InputField.text[i];
- // Check for opening indents
- if (character == AutoIndent.indentIncreaseCharacter)
- CurrentIndent++;
+ if (character == CSharpLexer.indentIncreaseCharacter)
+ CurrentIndent++;
- // Check for closing indents
- if (character == AutoIndent.indentDecreaseCharacter)
- CurrentIndent--;
- }
-
- // Dont allow negative indents
- if (CurrentIndent < 0)
- CurrentIndent = 0;
+ if (character == CSharpLexer.indentDecreaseCharacter)
+ CurrentIndent--;
}
+
+ if (CurrentIndent < 0)
+ CurrentIndent = 0;
}
- private void UpdateCurrentLineHighlight()
+ private void UpdateHighlight()
{
if (lineHighlightLocked)
return;
try
{
- // unity 2018.2 and older may need lineOffset as 0? not sure
- //int lineOffset = 1;
-
int caret = InputField.caretPosition - 1;
var lineHeight = inputText.textInfo.lineInfo[inputText.textInfo.characterInfo[0].lineNumber].lineHeight;
@@ -378,44 +277,33 @@ namespace Explorer.UI.Main.Pages.Console
private string SyntaxHighlightContent(string inputText)
{
- if (!InputTheme.allowSyntaxHighlighting)
- return inputText;
-
int offset = 0;
highlightedBuilder.Length = 0;
- foreach (var match in lexer.LexInputString(inputText))
+ foreach (var match in inputLexer.LexInputString(inputText))
{
- // Copy text before the match
for (int i = offset; i < match.startIndex; i++)
highlightedBuilder.Append(inputText[i]);
- // Add the opening color tag
highlightedBuilder.Append(match.htmlColor);
- // Copy text inbetween the match boundaries
for (int i = match.startIndex; i < match.endIndex; i++)
highlightedBuilder.Append(inputText[i]);
- // Add the closing color tag
highlightedBuilder.Append(CLOSE_COLOR_TAG);
- // Update offset
offset = match.endIndex;
}
- // Copy remaining text
for (int i = offset; i < inputText.Length; i++)
highlightedBuilder.Append(inputText[i]);
- // Convert to string
inputText = highlightedBuilder.ToString();
return inputText;
}
- // todo param is probably pointless
private void AutoIndentCaret()
{
if (CurrentIndent > 0)
@@ -429,7 +317,7 @@ namespace Explorer.UI.Main.Pages.Console
var indentMinusOne = indent.Substring(0, indent.Length - 1);
// get last index of {
- // check it on the next line if its not already
+ // chuck it on the next line if its not already
var text = InputField.text;
var sub = InputField.text.Substring(0, InputField.caretPosition);
var lastIndex = sub.LastIndexOf("{");
@@ -444,37 +332,9 @@ namespace Explorer.UI.Main.Pages.Console
}
// check if should add auto-close }
- int numOpen = 0;
- int numClose = 0;
- char prevChar = default;
- foreach (var _char in InputField.text)
- {
- if (_char == '{')
- {
- if (prevChar != default && (prevChar == '\\' || prevChar == '{'))
- {
- if (prevChar == '{')
- numOpen--;
- }
- else
- {
- numOpen++;
- }
- }
- else if (_char == '}')
- {
- if (prevChar != default && (prevChar == '\\' || prevChar == '}'))
- {
- if (prevChar == '}')
- numClose--;
- }
- else
- {
- numClose++;
- }
- }
- prevChar = _char;
- }
+ int numOpen = InputField.text.Where(x => x == CSharpLexer.indentIncreaseCharacter).Count();
+ int numClose = InputField.text.Where(x => x == CSharpLexer.indentDecreaseCharacter).Count();
+
if (numOpen > numClose)
{
// add auto-indent closing
@@ -490,14 +350,14 @@ namespace Explorer.UI.Main.Pages.Console
}
// Update line column and indent positions
- UpdateCurrentLineColumnIndent();
+ UpdateIndent();
inputText.text = InputField.text;
inputText.SetText(InputField.text, true);
inputText.Rebuild(CanvasUpdate.Prelayout);
InputField.ForceLabelUpdate();
InputField.Rebuild(CanvasUpdate.Prelayout);
- Refresh(true);
+ OnInputChanged(true);
}
private string GetAutoIndentTab(int amount)
@@ -510,26 +370,34 @@ namespace Explorer.UI.Main.Pages.Console
return tab;
}
+ private static Color caretColor = new Color32(255, 255, 255, 255);
+ private static Color textColor = new Color32(255, 255, 255, 255);
+ private static Color backgroundColor = new Color32(37, 37, 37, 255);
+ private static Color lineHighlightColor = new Color32(50, 50, 50, 255);
+ private static Color lineNumberBackgroundColor = new Color32(25, 25, 25, 255);
+ private static Color lineNumberTextColor = new Color32(180, 180, 180, 255);
+ private static Color scrollbarColor = new Color32(45, 50, 50, 255);
+
private void ApplyTheme()
{
- // Check for missing references
- if (!AllReferencesAssigned())
- throw new Exception("Cannot apply theme because one or more required component references are missing. ");
+ var highlightTextRect = inputHighlightText.GetComponent();
+ highlightTextRect.anchorMin = Vector2.zero;
+ highlightTextRect.anchorMax = Vector2.one;
+ highlightTextRect.offsetMin = Vector2.zero;
+ highlightTextRect.offsetMax = Vector2.zero;
- // Apply theme colors
- InputField.caretColor = InputTheme.caretColor;
- inputText.color = InputTheme.textColor;
- inputHighlightText.color = InputTheme.textColor;
- background.color = InputTheme.backgroundColor;
- lineHighlight.color = InputTheme.lineHighlightColor;
- lineNumberBackground.color = InputTheme.lineNumberBackgroundColor;
- lineText.color = InputTheme.lineNumberTextColor;
- scrollbar.color = InputTheme.scrollbarColor;
+ InputField.caretColor = caretColor;
+ inputText.color = textColor;
+ inputHighlightText.color = textColor;
+ background.color = backgroundColor;
+ lineHighlight.color = lineHighlightColor;
+ lineNumberBackground.color = lineNumberBackgroundColor;
+ lineText.color = lineNumberTextColor;
+ scrollbar.color = scrollbarColor;
}
private void ApplyLanguage()
{
- lexer.UseMatchers(CodeTheme.DelimiterSymbols, CodeTheme.Matchers);
}
private bool AllReferencesAssigned()
diff --git a/src/UI/Main/Pages/Console/Editor/CodeTheme.cs b/src/UI/Main/Pages/Console/Editor/CodeTheme.cs
deleted file mode 100644
index a2875f0..0000000
--- a/src/UI/Main/Pages/Console/Editor/CodeTheme.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using UnityEngine;
-using Explorer.UI.Main.Pages.Console.Lexer;
-using System.Runtime.InteropServices;
-
-namespace Explorer.UI.Main.Pages.Console
-{
- public static class CodeTheme
- {
- internal static readonly StringBuilder sharedBuilder = new StringBuilder();
-
- private static char[] delimiterSymbolCache = null;
- private static MatchLexer[] matchers = null;
-
- public static string languageName = "C#";
-
- public static string delimiterSymbols = "[ ] ( ) { } ; : , .";
-
- public static KeywordGroupMatch[] keywordGroups = new KeywordGroupMatch[]
- {
- // VALID KEYWORDS
-
- new KeywordGroupMatch()
- {
- highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f),
- caseSensitive = true,
- keywords = @"add as ascending await base bool break by byte
- case catch char checked const continue decimal default descending do dynamic
- else enum equals false finally fixed float for foreach from global goto group
- if in int into is join let lock long new null object on orderby out params
- partial ref remove return sbyte sealed select short sizeof stackalloc string
- struct switch this throw true try typeof uint ulong unchecked unsafe ushort
- using value var void where while yield"
- },
-
- // INVALID KEYWORDS (cannot use inside method scope)
-
- new KeywordGroupMatch()
- {
- highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f),
- caseSensitive = true,
- keywords = @"abstract async class delegate explicit extern get
- implicit interface internal namespace operator override private protected public
- readonly set static virtual volatile"
- }
- };
-
- ///
- /// A symbol group used to specify which symbols should be highlighted.
- ///
- public static SymbolGroupMatch symbolGroup = new SymbolGroupMatch
- {
- symbols = @"[ ] ( ) . ? : + - * / % & | ^ ~ = < > ++ -- && || << >> == != <= >=
- += -= *= /= %= &= |= ^= <<= >>= -> ?? =>",
- highlightColor = new Color(0.58f, 0.47f, 0.37f, 1.0f),
- };
-
- ///
- /// A number group used to specify whether numbers should be highlighted.
- ///
- public static NumberGroupMatch numberGroup = new NumberGroupMatch
- {
- highlightNumbers = true,
- highlightColor = new Color(0.58f, 0.33f, 0.33f, 1.0f)
- };
-
- ///
- /// A comment group used to specify which strings open and close comments.
- ///
- public static CommentGroupMatch commentGroup = new CommentGroupMatch
- {
- blockCommentEnd = @"*/",
- blockCommentStart = @"/*",
- lineCommentStart = @"//",
- lineCommentHasPresedence = true,
- highlightColor = new Color(0.34f, 0.65f, 0.29f, 1.0f),
- };
-
- ///
- /// A literal group used to specify whether quote strings should be highlighted.
- ///
- public static LiteralGroupMatch literalGroup = new LiteralGroupMatch
- {
- highlightLiterals = true,
- highlightColor = new Color(0.79f, 0.52f, 0.32f, 1.0f)
- };
-
- /////
- ///// Options group for all auto indent related settings.
- /////
- //public static AutoIndent autoIndent;
-
- // Properties
- internal static char[] DelimiterSymbols
- {
- get
- {
- if (delimiterSymbolCache == null)
- {
- // Split by space
- string[] symbols = delimiterSymbols.Split(' ');
-
- int count = 0;
-
- // Count the number of valid symbols
- for (int i = 0; i < symbols.Length; i++)
- if (symbols[i].Length == 1)
- count++;
-
- // Allocate array
- delimiterSymbolCache = new char[count];
-
- // Copy symbols
- for (int i = 0, index = 0; i < symbols.Length; i++)
- {
- // Require only 1 character
- if (symbols[i].Length == 1)
- {
- // Get the first character for the string
- delimiterSymbolCache[index] = symbols[i][0];
- index++;
- }
- }
- }
- return delimiterSymbolCache;
- }
- }
-
- internal static MatchLexer[] Matchers
- {
- get
- {
- if (matchers == null)
- {
- List matcherList = new List
- {
- commentGroup,
- symbolGroup,
- numberGroup,
- literalGroup
- };
- matcherList.AddRange(keywordGroups);
-
- matchers = matcherList.ToArray();
- }
- return matchers;
- }
- }
-
- // Methods
- internal static void Invalidate()
- {
- foreach (KeywordGroupMatch group in keywordGroups)
- group.Invalidate();
-
- symbolGroup.Invalidate();
- commentGroup.Invalidate();
- numberGroup.Invalidate();
- literalGroup.Invalidate();
- }
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/InputTheme.cs b/src/UI/Main/Pages/Console/Editor/InputTheme.cs
deleted file mode 100644
index 9299579..0000000
--- a/src/UI/Main/Pages/Console/Editor/InputTheme.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using UnityEngine;
-
-namespace Explorer.UI.Main.Pages.Console
-{
- public static class InputTheme
- {
- public static bool allowSyntaxHighlighting = true;
-
- public static Color caretColor = new Color32(255, 255, 255, 255);
- public static Color textColor = new Color32(255, 255, 255, 255);
- public static Color backgroundColor = new Color32(37, 37, 37, 255);
- public static Color lineHighlightColor = new Color32(50, 50, 50, 255);
- public static Color lineNumberBackgroundColor = new Color32(25, 25, 25, 255);
- public static Color lineNumberTextColor = new Color32(180, 180, 180, 255);
- public static Color scrollbarColor = new Color32(45, 50, 50, 255);
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/CommentGroupMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/CommentGroupMatch.cs
deleted file mode 100644
index 8ba8b40..0000000
--- a/src/UI/Main/Pages/Console/Editor/Lexer/CommentGroupMatch.cs
+++ /dev/null
@@ -1,221 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Explorer.Unstrip.ColorUtility;
-using UnityEngine;
-
-namespace Explorer.UI.Main.Pages.Console.Lexer
-{
- ///
- /// Used to match line and block comments.
- ///
- public sealed class CommentGroupMatch : MatchLexer
- {
- [NonSerialized]
- private string htmlColor = null;
-
- // Public
- ///
- /// The string that denotes the start of a line comment.
- /// Leave this value empty if line comments should not be highlighted.
- ///
- public string lineCommentStart;
- ///
- /// The string that denotes the start of a block comment.
- /// Leave this value empty if block comments should not be highlighted.
- ///
- public string blockCommentStart;
- ///
- /// The string that denotes the end of a block comment.
- ///
- public string blockCommentEnd;
- ///
- /// The color that comments will be highlighted with.
- ///
- public Color highlightColor = Color.black;
-
- public bool lineCommentHasPresedence = true;
-
- // Properties
- ///
- /// Retrusn a value indicating whether any comment highlighting is enabled.
- /// A valid line or block comment start string must be specified in order for comment highlighting to be enabled.
- ///
- public bool HasCommentHighlighting
- {
- get
- {
- return string.IsNullOrEmpty(lineCommentStart) == false ||
- string.IsNullOrEmpty(blockCommentStart) == false;
- }
- }
-
- ///
- /// Get the html tag color that comments will be highlighted with.
- ///
- public override string HTMLColor
- {
- get
- {
- // Build html color string
- if (htmlColor == null)
- htmlColor = "<#" + highlightColor.ToHex() + ">";
-
- return htmlColor;
- }
- }
-
- ///
- /// Returns an enumerable collection of characters from this group that can act as delimiter symbols when they appear after a keyword.
- ///
- public override IEnumerable SpecialStartCharacters
- {
- get
- {
- if (string.IsNullOrEmpty(lineCommentStart) == false)
- yield return lineCommentStart[0];
-
- if (string.IsNullOrEmpty(blockCommentEnd) == false)
- yield return blockCommentEnd[0];
- }
- }
-
- ///
- /// Returns an enumerable collection of characters from this group that can act as delimiter symbols when they appear before a keyword.
- ///
- public override IEnumerable SpecialEndCharacters
- {
- get
- {
- if (string.IsNullOrEmpty(blockCommentEnd) == false)
- yield return blockCommentEnd[blockCommentEnd.Length - 1];
- }
- }
-
- // Methods
- ///
- /// Causes the cached values to be reloaded.
- /// Useful for editor visualisation.
- ///
- public override void Invalidate()
- {
- this.htmlColor = null;
- }
-
- ///
- /// Returns true if the lexer input contains a valid comment format as the next character sequence.
- ///
- /// The input lexer
- /// True if a comment was found or false if not
- public override bool IsImplicitMatch(ILexer lexer)
- {
- if (lineCommentHasPresedence == true)
- {
- // Parse line comments then block comments
- if (IsLineCommentMatch(lexer) == true ||
- IsBlockCommentMatch(lexer) == true)
- return true;
- }
- else
- {
- // Parse block comments then line coments
- if (IsBlockCommentMatch(lexer) == true ||
- IsLineCommentMatch(lexer) == true)
- return true;
- }
-
- // Not a comment
- return false;
- }
-
- private bool IsLineCommentMatch(ILexer lexer)
- {
- // Check for line comment
- if (string.IsNullOrEmpty(lineCommentStart) == false)
- {
- lexer.Rollback();
-
- bool match = true;
-
- for (int i = 0; i < lineCommentStart.Length; i++)
- {
- if (lineCommentStart[i] != lexer.ReadNext())
- {
- match = false;
- break;
- }
- }
-
- // Check for valid match
- if (match == true)
- {
- // Read until end
- while (IsEndLineOrEndFile(lexer, lexer.ReadNext()) == false) ;
-
- // Matched a single line comment
- return true;
- }
- }
- return false;
- }
-
- private bool IsBlockCommentMatch(ILexer lexer)
- {
- // Check for block comment
- if (string.IsNullOrEmpty(blockCommentStart) == false)
- {
- lexer.Rollback();
-
- bool match = true;
-
- for (int i = 0; i < blockCommentStart.Length; i++)
- {
- if (blockCommentStart[i] != lexer.ReadNext())
- {
- match = false;
- break;
- }
- }
-
- // Check for valid match
- if (match == true)
- {
- // Read until end or closing block
- while (IsEndLineOrString(lexer, blockCommentEnd) == false) ;
-
- // Matched a multi-line block commend
- return true;
- }
- }
- return false;
- }
-
- private bool IsEndLineOrEndFile(ILexer lexer, char character)
- {
- if (lexer.EndOfStream == true ||
- character == '\n' ||
- character == '\r')
- {
- // Line end or file end
- return true;
- }
- return false;
- }
-
- private bool IsEndLineOrString(ILexer lexer, string endString)
- {
- for (int i = 0; i < endString.Length; i++)
- {
- // Check for end of stream
- if (lexer.EndOfStream == true)
- return true;
-
- // Check for matching end string
- if (endString[i] != lexer.ReadNext())
- return false;
- }
-
- // We matched the end string
- return true;
- }
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/CommentMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/CommentMatch.cs
new file mode 100644
index 0000000..82533be
--- /dev/null
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/CommentMatch.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using Explorer.Unstrip.ColorUtility;
+using UnityEngine;
+
+namespace Explorer.UI.Main.Pages.Console.Lexer
+{
+ public sealed class CommentMatch : MatchLexer
+ {
+ public string lineCommentStart = @"//";
+ public string blockCommentStart = @"/*";
+ public string blockCommentEnd = @"*/";
+
+ public override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f);
+ public override IEnumerable StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] };
+ public override IEnumerable EndChars => new char[] { blockCommentEnd[0] };
+ public override bool IsImplicitMatch(ILexer lexer) => IsMatch(lexer, lineCommentStart) || IsMatch(lexer, blockCommentStart);
+
+ private bool IsMatch(ILexer lexer, string commentType)
+ {
+ if (!string.IsNullOrEmpty(commentType))
+ {
+ lexer.Rollback();
+
+ bool match = true;
+ for (int i = 0; i < commentType.Length; i++)
+ {
+ if (commentType[i] != lexer.ReadNext())
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ {
+ // Read until end
+ while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) ;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private bool IsEndLineOrEndFile(ILexer lexer, char character) => lexer.EndOfStream || character == '\n' || character == '\r';
+ }
+}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/ILexer.cs b/src/UI/Main/Pages/Console/Editor/Lexer/ILexer.cs
index 112528f..7a506d0 100644
--- a/src/UI/Main/Pages/Console/Editor/Lexer/ILexer.cs
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/ILexer.cs
@@ -6,62 +6,20 @@ using System.Threading.Tasks;
namespace Explorer.UI.Main.Pages.Console.Lexer
{
- // Types
- ///
- /// Represents a keyword position where a special character may appear.
- ///
public enum SpecialCharacterPosition
{
- ///
- /// The special character may appear before a keyword.
- ///
Start,
- ///
- /// The special character may appear after a keyword.
- ///
End,
};
- ///
- /// Represents a streamable lexer input which can be examined by matchers.
- ///
public interface ILexer
{
- // Properties
- ///
- /// Returns true if there are no more characters to read.
- ///
bool EndOfStream { get; }
-
- ///
- /// Returns the previously read character or '\0' if there is no previous character.
- ///
char Previous { get; }
- // Methods
- ///
- /// Attempt to read the next character.
- ///
- /// The next character in the stream or '\0' if the end of stream is reached
char ReadNext();
-
- ///
- /// Causes the lexer to return its state to a previously commited state.
- ///
- /// Use -1 to return to the last commited state or a positive number to represent the number of characters to rollback
void Rollback(int amount = -1);
-
- ///
- /// Causes all read characters to be commited meaning that rollback will return to this lexer state.
- ///
void Commit();
-
- ///
- /// Determines whether the specified character is considered a special symbol by the lexer meaning that it is able to act as a delimiter.
- ///
- /// The character to check
- /// The character position to check. This determines whether the character may appear at the start or end of a keyword
- /// True if the character is a valid delimiter or false if not
bool IsSpecialSymbol(char character, SpecialCharacterPosition position = SpecialCharacterPosition.Start);
}
}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/InputStringLexer.cs b/src/UI/Main/Pages/Console/Editor/Lexer/InputLexer.cs
similarity index 74%
rename from src/UI/Main/Pages/Console/Editor/Lexer/InputStringLexer.cs
rename to src/UI/Main/Pages/Console/Editor/Lexer/InputLexer.cs
index 6afc22c..6c88b5b 100644
--- a/src/UI/Main/Pages/Console/Editor/Lexer/InputStringLexer.cs
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/InputLexer.cs
@@ -6,17 +6,15 @@ using UnityEngine;
namespace Explorer.UI.Main.Pages.Console.Lexer
{
- internal struct InputStringMatchInfo
+ internal struct LexerMatchInfo
{
- // Public
public int startIndex;
public int endIndex;
public string htmlColor;
}
- internal class InputStringLexer : ILexer
+ internal class InputLexer : ILexer
{
- // Private
private string inputString = null;
private MatchLexer[] matchers = null;
private readonly HashSet specialStartSymbols = new HashSet();
@@ -26,7 +24,6 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
private int currentIndex = 0;
private int currentLookaheadIndex = 0;
- // Properties
public bool EndOfStream
{
get { return currentLookaheadIndex >= inputString.Length; }
@@ -37,102 +34,82 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
get { return previous; }
}
- // Methods
public void UseMatchers(char[] delimiters, MatchLexer[] matchers)
{
- // Store matchers
this.matchers = matchers;
- // Clear old symbols
specialStartSymbols.Clear();
specialEndSymbols.Clear();
- // Check for any delimiter characters
if (delimiters != null)
{
- // Add delimiters
foreach (char character in delimiters)
{
- // Add to start
if (specialStartSymbols.Contains(character) == false)
specialStartSymbols.Add(character);
- // Add to end
if (specialEndSymbols.Contains(character) == false)
specialEndSymbols.Add(character);
}
}
- // Check for any matchers
if (matchers != null)
{
- // Add all special symbols which can act as a delimiter
foreach (MatchLexer lexer in matchers)
{
- foreach (char special in lexer.SpecialStartCharacters)
+ foreach (char special in lexer.StartChars)
if (specialStartSymbols.Contains(special) == false)
specialStartSymbols.Add(special);
- foreach (char special in lexer.SpecialEndCharacters)
+ foreach (char special in lexer.EndChars)
if (specialEndSymbols.Contains(special) == false)
specialEndSymbols.Add(special);
}
}
}
- public IEnumerable LexInputString(string input)
+ public IEnumerable LexInputString(string input)
{
if (input == null || matchers == null || matchers.Length == 0)
yield break;
- // Store the input string
this.inputString = input;
this.current = ' ';
this.previous = ' ';
this.currentIndex = 0;
this.currentLookaheadIndex = 0;
- // Process the input string
while (EndOfStream == false)
{
bool didMatchLexer = false;
- // Read any white spaces
ReadWhiteSpace();
- // Process each matcher
foreach (MatchLexer matcher in matchers)
{
- // Get the current index
int startIndex = currentIndex;
- // Try to match
bool isMatched = matcher.IsMatch(this);
if (isMatched == true)
{
- // Get the end index of the match
int endIndex = currentIndex;
- // Set matched flag
didMatchLexer = true;
- // Register the match
- yield return new InputStringMatchInfo
+ yield return new LexerMatchInfo
{
startIndex = startIndex,
endIndex = endIndex,
- htmlColor = matcher.HTMLColor,
+ htmlColor = matcher.HexColor,
};
- // Move to next character
break;
}
}
if (didMatchLexer == false)
{
- // Move to next
ReadNext();
Commit();
}
@@ -141,14 +118,11 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
public char ReadNext()
{
- // Check for end of stream
if (EndOfStream == true)
return '\0';
- // Update previous character
previous = current;
- // Get the character
current = inputString[currentLookaheadIndex];
currentLookaheadIndex++;
@@ -159,7 +133,6 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
{
if (amount == -1)
{
- // Revert to index
currentLookaheadIndex = currentIndex;
}
else
@@ -193,14 +166,11 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
private void ReadWhiteSpace()
{
- // Read until white space
while (char.IsWhiteSpace(ReadNext()) == true)
{
- // Consume the char
Commit();
}
- // Return lexer state
Rollback();
}
}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/KeywordGroupMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/KeywordGroupMatch.cs
deleted file mode 100644
index 1f8254c..0000000
--- a/src/UI/Main/Pages/Console/Editor/Lexer/KeywordGroupMatch.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using Explorer.Unstrip.ColorUtility;
-using UnityEngine;
-
-namespace Explorer.UI.Main.Pages.Console.Lexer
-{
- ///
- /// A matcher that checks for a number of predefined keywords in the lexer stream.
- ///
- public sealed class KeywordGroupMatch : MatchLexer
- {
- // Private
- private static readonly HashSet shortlist = new HashSet();
- private static readonly Stack removeList = new Stack();
- private string[] keywordCache = null;
- private string htmlColor = null;
-
- // Public
- ///
- /// Used for editor gui only. Has no purpose other than to give the inspector foldout a nice name
- ///
- public string group = "Keyword Group"; // This value is not used - it just gives the inspector foldout a nice name
- ///
- /// A string containing one or more keywords separated by a space character that will be used by this matcher.
- ///
- public string keywords;
- ///
- /// The color that any matched keywords will be highlighted.
- ///
- public Color highlightColor = Color.black;
- ///
- /// Should keyword matching be case sensitive.
- ///
- public bool caseSensitive = true;
-
- // Properties
- ///
- /// Get a value indicating whether keyword highlighting is enabled based upon the number of valid keywords found.
- ///
- public bool HasKeywordHighlighting
- {
- get
- {
- // Check for valid keyword
- if (string.IsNullOrEmpty(keywords) == false)
- return true;
-
- return false;
- }
- }
-
- ///
- /// Get the html formatted color tag that any matched keywords will be highlighted with.
- ///
- public override string HTMLColor
- {
- get
- {
- // Get html color
- if (htmlColor == null)
- htmlColor = "<#" + highlightColor.ToHex() + ">";
-
- return htmlColor;
- }
- }
-
- // Methods
- ///
- /// Causes any cached data to be reloaded.
- ///
- public override void Invalidate()
- {
- this.htmlColor = null;
- }
-
- ///
- /// Check whether the specified lexer has a valid keyword at its current position.
- ///
- /// The input lexer to check
- /// True if the stream has a keyword or false if not
- public override bool IsImplicitMatch(ILexer lexer)
- {
- // Make sure cache is built
- BuildKeywordCache();
-
- // Require whitespace before character
- if (char.IsWhiteSpace(lexer.Previous) == false &&
- lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End) == false)
- return false;
-
- // Clear old data
- shortlist.Clear();
-
- // Read the first character
- int currentIndex = 0;
- char currentChar = lexer.ReadNext();
-
- // Add to shortlist
- for (int i = 0; i < keywordCache.Length; i++)
- if (CompareChar(keywordCache[i][0], currentChar) == true)
- shortlist.Add(keywordCache[i]);
-
- // Check for no matches we can skip the heavy work quickly
- if (shortlist.Count == 0)
- return false;
-
- do
- {
- // Check for end of stream
- if (lexer.EndOfStream == true)
- {
- RemoveLongStrings(currentIndex + 1);
- break;
- }
-
- // Read next character
- currentChar = lexer.ReadNext();
- currentIndex++;
-
- // Check for end of word
- if (char.IsWhiteSpace(currentChar) == true ||
- lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start) == true)
- {
- // Finalize any matching candidates and undo the reading of the space or special character
- RemoveLongStrings(currentIndex);
- lexer.Rollback(1);
- break;
- }
-
- // Check for shortlist match
- foreach (string keyword in shortlist)
- {
- if (currentIndex >= keyword.Length ||
- CompareChar(keyword[currentIndex], currentChar) == false)
- {
- removeList.Push(keyword);
- }
- }
-
- // Remove from short list
- while (removeList.Count > 0)
- shortlist.Remove(removeList.Pop());
- }
- while (shortlist.Count > 0);
-
- // Check for any words in the shortlist
- return shortlist.Count > 0;
- }
-
- private void RemoveLongStrings(int length)
- {
- foreach (string keyword in shortlist)
- {
- if (keyword.Length > length)
- {
- removeList.Push(keyword);
- }
- }
-
- // Remove from short list
- while (removeList.Count > 0)
- shortlist.Remove(removeList.Pop());
- }
-
- private void BuildKeywordCache()
- {
- // Check if we need to build the cache
- if (keywordCache == null)
- {
- // Get keyowrds and insert them into a cache array for quick reference
- var kwSplit = keywords.Split(' ');
-
- var list = new List();
- foreach (var kw in kwSplit)
- {
- if (!string.IsNullOrEmpty(kw) && kw.Length > 0)
- {
- list.Add(kw);
- }
- }
- keywordCache = list.ToArray();
- }
- }
-
- private bool CompareChar(char a, char b)
- {
- // Check for direct match
- if (a == b)
- return true;
-
- // Check for case sensitive
- if (caseSensitive == false)
- {
- if (char.ToUpper(a, CultureInfo.CurrentCulture) ==
- char.ToUpper(b, CultureInfo.CurrentCulture))
- {
- // Check for match ignoring case
- return true;
- }
- }
- return false;
- }
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/KeywordMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/KeywordMatch.cs
new file mode 100644
index 0000000..893b6fa
--- /dev/null
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/KeywordMatch.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using Explorer.Unstrip.ColorUtility;
+using ExplorerBeta;
+using UnityEngine;
+
+namespace Explorer.UI.Main.Pages.Console.Lexer
+{
+ public sealed class KeywordMatch : MatchLexer
+ {
+ public string keywords;
+
+ public override Color HighlightColor => this.highlightColor;
+ public Color highlightColor;
+
+ private readonly HashSet shortlist = new HashSet();
+ private readonly Stack removeList = new Stack();
+ private string[] keywordCache = null;
+
+ public override bool IsImplicitMatch(ILexer lexer)
+ {
+ BuildKeywordCache();
+
+ if (!char.IsWhiteSpace(lexer.Previous) &&
+ !lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End))
+ return false;
+
+ shortlist.Clear();
+
+ int currentIndex = 0;
+ char currentChar = lexer.ReadNext();
+
+ for (int i = 0; i < keywordCache.Length; i++)
+ if (CompareChar(keywordCache[i][0], currentChar))
+ shortlist.Add(keywordCache[i]);
+
+ if (shortlist.Count == 0)
+ return false;
+
+ do
+ {
+ if (lexer.EndOfStream)
+ {
+ RemoveLongStrings(currentIndex + 1);
+ break;
+ }
+
+ currentChar = lexer.ReadNext();
+ currentIndex++;
+
+ if (char.IsWhiteSpace(currentChar) ||
+ lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start))
+ {
+ RemoveLongStrings(currentIndex);
+ lexer.Rollback(1);
+ break;
+ }
+
+ foreach (string keyword in shortlist)
+ {
+ if (currentIndex >= keyword.Length ||
+ !CompareChar(keyword[currentIndex], currentChar))
+ {
+ removeList.Push(keyword);
+ }
+ }
+
+ while (removeList.Count > 0)
+ shortlist.Remove(removeList.Pop());
+ }
+ while (shortlist.Count > 0);
+
+ return shortlist.Count > 0;
+ }
+
+ private void RemoveLongStrings(int length)
+ {
+ foreach (string keyword in shortlist)
+ if (keyword.Length > length)
+ removeList.Push(keyword);
+
+ while (removeList.Count > 0)
+ shortlist.Remove(removeList.Pop());
+ }
+
+ private void BuildKeywordCache()
+ {
+ if (keywordCache == null)
+ {
+ var kwSplit = keywords.Split(' ');
+
+ var list = new List();
+ foreach (var kw in kwSplit)
+ {
+ if (!string.IsNullOrEmpty(kw) && kw.Length > 0)
+ {
+ list.Add(kw);
+ }
+ }
+ keywordCache = list.ToArray();
+ }
+ }
+
+ private bool CompareChar(char a, char b) =>
+ (a == b) || (char.ToUpper(a, CultureInfo.CurrentCulture) == char.ToUpper(b, CultureInfo.CurrentCulture));
+ }
+}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/LiteralGroupMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/LiteralGroupMatch.cs
deleted file mode 100644
index 4601148..0000000
--- a/src/UI/Main/Pages/Console/Editor/Lexer/LiteralGroupMatch.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Explorer.Unstrip.ColorUtility;
-using UnityEngine;
-
-namespace Explorer.UI.Main.Pages.Console.Lexer
-{
- ///
- /// A matcher that checks for quote strings in the lexer stream.
- ///
- [Serializable]
- public sealed class LiteralGroupMatch : MatchLexer
- {
- // Private
- private string htmlColor = null;
-
- // Public
- ///
- /// Should literal be highlighted.
- /// When true, any text surrounded by double quotes will be highlighted.
- ///
- public bool highlightLiterals = true;
- ///
- /// The color that any matched literals will be highlighted.
- ///
- public Color highlightColor = Color.black;
-
- // Properties
- ///
- /// Get a value indicating whether literal highlighting is enabled.
- ///
- public bool HasLiteralHighlighting
- {
- get { return highlightLiterals; }
- }
-
- ///
- /// Get the html formatted color tag that any matched literals will be highlighted with.
- ///
- public override string HTMLColor
- {
- get
- {
- if (htmlColor == null)
- htmlColor = "<#" + highlightColor.ToHex() + ">";
-
- return htmlColor;
- }
- }
-
- ///
- /// Returns special symbols that can act as delimiters when appearing before a word.
- /// In this case '"' will be returned.
- ///
- public override IEnumerable SpecialStartCharacters
- {
- get
- {
- yield return '"';
- }
- }
-
- ///
- /// Returns special symbols that can act as delimiters when appearing after a word.
- /// In this case '"' will be returned.
- ///
- public override IEnumerable SpecialEndCharacters
- {
- get
- {
- yield return '"';
- }
- }
-
- // Methods
- ///
- /// Causes any cached data to be reloaded.
- ///
- public override void Invalidate()
- {
- this.htmlColor = null;
- }
-
- ///
- /// Check whether the specified lexer has a valid literal at its current position.
- ///
- /// The input lexer to check
- /// True if the stream has a literal or false if not
- public override bool IsImplicitMatch(ILexer lexer)
- {
- // Skip highlighting
- if (highlightLiterals == false)
- return false;
-
- // Check for quote
- if (lexer.ReadNext() == '"')
- {
- // Read all characters inside the quote
- while (IsClosingQuoteOrEndFile(lexer, lexer.ReadNext()) == false) ;
-
- // Found a valid literal
- return true;
- }
- return false;
- }
-
- private bool IsClosingQuoteOrEndFile(ILexer lexer, char character)
- {
- if (lexer.EndOfStream == true ||
- character == '"')
- {
- // We have found the end of the file or quote
- return true;
- }
- return false;
- }
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/MatchLexer.cs b/src/UI/Main/Pages/Console/Editor/Lexer/MatchLexer.cs
index 97560c2..09d6ede 100644
--- a/src/UI/Main/Pages/Console/Editor/Lexer/MatchLexer.cs
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/MatchLexer.cs
@@ -1,61 +1,33 @@
using System;
using System.Collections.Generic;
+using Explorer.Unstrip.ColorUtility;
+using ExplorerBeta;
using UnityEngine;
namespace Explorer.UI.Main.Pages.Console.Lexer
{
public abstract class MatchLexer
{
- ///
- /// Get the html formatted color tag that any matched text will be highlighted with.
- ///
- public abstract string HTMLColor { get; }
+ public abstract Color HighlightColor { get; }
- ///
- /// Get an enumerable collection of special characters that can act as delimiter symbols when they appear before a word.
- ///
- public virtual IEnumerable SpecialStartCharacters { get { yield break; } }
+ public string HexColor => htmlColor ?? (htmlColor = "<#" + HighlightColor.ToHex() + ">");
+ private string htmlColor = null;
- ///
- /// Get an enumerable collection of special characters that can act as delimiter symbols when they appear after a word.
- ///
- public virtual IEnumerable SpecialEndCharacters { get { yield break; } }
+ public virtual IEnumerable StartChars { get { yield break; } }
+ public virtual IEnumerable EndChars { get { yield break; } }
- // Methods
- ///
- /// Checks the specified lexers current position for a certain sequence of characters as defined by the inheriting matcher.
- ///
- ///
- ///
public abstract bool IsImplicitMatch(ILexer lexer);
- ///
- /// Causes the matcher to invalidate any cached data forcing it to be regenerated or reloaded.
- ///
- public virtual void Invalidate() { }
-
- ///
- /// Attempts to check for a match in the specified lexer.
- ///
- /// The lexer that will be checked
- /// True if a match was found or false if not
public bool IsMatch(ILexer lexer)
{
- // Check for implicit match
- bool match = IsImplicitMatch(lexer);
-
- if (match == true)
+ if (IsImplicitMatch(lexer))
{
- // Consume read tokens
lexer.Commit();
- }
- else
- {
- // Revert lexer state
- lexer.Rollback();
+ return true;
}
- return match;
+ lexer.Rollback();
+ return false;
}
}
}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/NumberGroupMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/NumberGroupMatch.cs
deleted file mode 100644
index 2effc0a..0000000
--- a/src/UI/Main/Pages/Console/Editor/Lexer/NumberGroupMatch.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using UnityEngine;
-using Explorer.Unstrip.ColorUtility;
-
-namespace Explorer.UI.Main.Pages.Console.Lexer
-{
- ///
- /// A matcher that checks for any numbers that appear in the lexer stream.
- ///
- public sealed class NumberGroupMatch : MatchLexer
- {
- // Private
- private string htmlColor = null;
-
- // Public
- ///
- /// Should number highlighting be used.
- /// When false, numbers will appear in the default text color as defined by the current editor theme.
- ///
- public bool highlightNumbers = true;
- ///
- /// The color that any matched numbers will be highlighted.
- ///
- public Color highlightColor = Color.black;
-
- // Properties
- ///
- /// Get a value indicating whether keyword highlighting is enabled.
- ///
- public bool HasNumberHighlighting
- {
- get { return highlightNumbers; }
- }
-
- ///
- /// Get the html formatted color tag that any matched numbers will be highlighted with.
- ///
- public override string HTMLColor
- {
- get
- {
- if (htmlColor == null)
- htmlColor = "<#" + highlightColor.ToHex() + ">";
-
- return htmlColor;
- }
- }
-
- // Methods
- ///
- /// Causes any cached data to be reloaded.
- ///
- public override void Invalidate()
- {
- this.htmlColor = null;
- }
-
- ///
- /// Check whether the specified lexer has a valid number sequence at its current position.
- ///
- /// The input lexer to check
- /// True if the stream has a number sequence or false if not
- public override bool IsImplicitMatch(ILexer lexer)
- {
- // Skip highlighting
- if (highlightNumbers == false)
- return false;
-
- // Require whitespace or symbols before numbers
- if (char.IsWhiteSpace(lexer.Previous) == false &&
- lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End) == false)
- {
- // There is some other character before the potential number
- return false;
- }
-
- bool matchedNumber = false;
-
- // Consume the number characters
- while (lexer.EndOfStream == false)
- {
- // Check for valid numerical character
- if (IsNumberOrDecimalPoint(lexer.ReadNext()) == true)
- {
- // We have found a number or decimal
- matchedNumber = true;
- lexer.Commit();
- }
- else
- {
- lexer.Rollback();
- break;
- }
- }
-
- return matchedNumber;
- }
-
- private bool IsNumberOrDecimalPoint(char character) => char.IsNumber(character) || character == '.';
- }
-
-}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/NumberMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/NumberMatch.cs
new file mode 100644
index 0000000..0af3437
--- /dev/null
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/NumberMatch.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Explorer.Unstrip.ColorUtility;
+
+namespace Explorer.UI.Main.Pages.Console.Lexer
+{
+ public sealed class NumberMatch : MatchLexer
+ {
+ public override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f);
+
+ public override bool IsImplicitMatch(ILexer lexer)
+ {
+ if (!char.IsWhiteSpace(lexer.Previous) &&
+ !lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End))
+ {
+ return false;
+ }
+
+ bool matchedNumber = false;
+
+ while (!lexer.EndOfStream)
+ {
+ if (IsNumberOrDecimalPoint(lexer.ReadNext()))
+ {
+ matchedNumber = true;
+ lexer.Commit();
+ }
+ else
+ {
+ lexer.Rollback();
+ break;
+ }
+ }
+
+ return matchedNumber;
+ }
+
+ private bool IsNumberOrDecimalPoint(char character) => char.IsNumber(character) || character == '.';
+ }
+
+}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/StringMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/StringMatch.cs
new file mode 100644
index 0000000..3f528b0
--- /dev/null
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/StringMatch.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Explorer.Unstrip.ColorUtility;
+using UnityEngine;
+
+namespace Explorer.UI.Main.Pages.Console.Lexer
+{
+ public sealed class StringMatch : MatchLexer
+ {
+ public override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f);
+
+ public override IEnumerable StartChars { get { yield return '"'; } }
+ public override IEnumerable EndChars { get { yield return '"'; } }
+
+ public override bool IsImplicitMatch(ILexer lexer)
+ {
+ if (lexer.ReadNext() == '"')
+ {
+ while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) ;
+ return true;
+ }
+ return false;
+ }
+
+ private bool IsClosingQuoteOrEndFile(ILexer lexer, char character)
+ {
+ if (lexer.EndOfStream == true ||
+ character == '"')
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/SymbolGroupMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/SymbolGroupMatch.cs
deleted file mode 100644
index 53dbced..0000000
--- a/src/UI/Main/Pages/Console/Editor/Lexer/SymbolGroupMatch.cs
+++ /dev/null
@@ -1,223 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Explorer.Unstrip.ColorUtility;
-using ExplorerBeta;
-using UnityEngine;
-
-namespace Explorer.UI.Main.Pages.Console.Lexer
-{
- ///
- /// A matcher that checks for a number of predefined symbols in the lexer stream.
- ///
- [Serializable]
- public sealed class SymbolGroupMatch : MatchLexer
- {
- // Private
- private static readonly List shortlist = new List();
- private static readonly Stack removeList = new Stack();
- [NonSerialized]
- private string[] symbolCache = null;
- [NonSerialized]
- private string htmlColor = null;
-
- // Public
- ///
- /// A string containing one or more symbols separated by a space character that will be used by the matcher.
- /// Symbols can be one or more characters long and should not contain numbers or letters.
- ///
- public string symbols;
- ///
- /// The color that any matched symbols will be highlighted.
- ///
- public Color highlightColor = Color.black;
-
- // Properties
- ///
- /// Get a value indicating whether symbol highlighting is enabled based upon the number of valid symbols found.
- ///
- public bool HasSymbolHighlighting
- {
- get { return symbols.Length > 0; }
- }
-
- ///
- /// Get the html formatted color tag that any matched symbols will be highlighted with.
- ///
- public override string HTMLColor
- {
- get
- {
- // Get html color
- if (htmlColor == null)
- htmlColor = "<#" + highlightColor.ToHex() + ">";
-
- return htmlColor;
- }
- }
-
- ///
- /// Returns special symbols that can act as delimiters when appearing before a word.
- ///
- public override IEnumerable SpecialStartCharacters
- {
- get
- {
- // Make sure cahce is created
- BuildSymbolCache();
-
- // Get the first character of each symbol
- foreach (string symbol in symbolCache.Where(x => x.Length > 0))
- yield return symbol[0];
- }
- }
-
- ///
- /// Returns special symbols that can act as delimiters when appearing after a word.
- /// In this case '"' will be returned.
- ///
- public override IEnumerable SpecialEndCharacters
- {
- get
- {
- // Make sure cahce is created
- BuildSymbolCache();
-
- // Get the first character of each symbol
- foreach (string symbol in symbolCache.Where(x => x.Length > 0))
- yield return symbol[0];
- }
- }
-
- // Methods
- ///
- /// Causes any cached data to be reloaded.
- ///
- public override void Invalidate()
- {
- this.htmlColor = null;
- }
-
- ///
- /// Checks whether the specified lexer has a valid symbol at its current posiiton.
- ///
- /// The input lexer to check
- /// True if the stream has a symbol or false if not
- public override bool IsImplicitMatch(ILexer lexer)
- {
- // Make sure cache is created
- BuildSymbolCache();
-
- if (lexer == null)
- {
- return false;
- }
-
- // Require whitespace, letter or digit before symbol
- if (char.IsWhiteSpace(lexer.Previous) == false &&
- char.IsLetter(lexer.Previous) == false &&
- char.IsDigit(lexer.Previous) == false &&
- lexer.IsSpecialSymbol(lexer.Previous, SpecialCharacterPosition.End) == false)
- return false;
-
- // Clear old data
- shortlist.Clear();
-
- // Read the first character
- int currentIndex = 0;
- char currentChar = lexer.ReadNext();
-
- // Add to shortlist
- for (int i = symbolCache.Length - 1; i >= 0; i--)
- {
- if (symbolCache[i][0] == currentChar)
- shortlist.Add(symbolCache[i]);
- }
-
- // No potential matches
- if (shortlist.Count == 0)
- return false;
-
- do
- {
- // Check for end of stream
- if (lexer.EndOfStream == true)
- {
- RemoveLongStrings(currentIndex + 1);
- break;
- }
-
- // Read next character
- currentChar = lexer.ReadNext();
- currentIndex++;
-
- if (char.IsWhiteSpace(currentChar) == true ||
- char.IsLetter(currentChar) == true ||
- char.IsDigit(currentChar) == true ||
- lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start) == true)
- {
- RemoveLongStrings(currentIndex);
- lexer.Rollback(1);
- break;
- }
-
- // Check for shortlist match
- foreach (string symbol in shortlist)
- {
- if (currentIndex >= symbol.Length ||
- symbol[currentIndex] != currentChar)
- {
- removeList.Push(symbol);
- }
- }
-
- // Remove from short list
- while (removeList.Count > 0)
- shortlist.Remove(removeList.Pop());
- }
- while (shortlist.Count > 0);
-
- // Check for any words in the shortlist
- return shortlist.Count > 0;
- }
-
- private void RemoveLongStrings(int length)
- {
- foreach (string keyword in shortlist)
- {
- if (keyword.Length > length)
- {
- removeList.Push(keyword);
- }
- }
-
- // Remove from short list
- while (removeList.Count > 0)
- shortlist.Remove(removeList.Pop());
- }
-
- private void BuildSymbolCache()
- {
- if (string.IsNullOrEmpty(symbols))
- {
- ExplorerCore.LogWarning("Symbol cache is null!");
- symbolCache = new string[0];
- }
- else
- {
- // Get symbols and insert them into a cache array for quick reference
- var symSplit = symbols.Split(' ');
- var list = new List();
- foreach (var sym in symSplit)
- {
- if (!string.IsNullOrEmpty(sym) && sym.Length > 0)
- {
- list.Add(sym);
- }
- }
- symbolCache = list.ToArray();
- }
- }
- }
-}
diff --git a/src/UI/Main/Pages/Console/Editor/Lexer/SymbolMatch.cs b/src/UI/Main/Pages/Console/Editor/Lexer/SymbolMatch.cs
new file mode 100644
index 0000000..9ff07ff
--- /dev/null
+++ b/src/UI/Main/Pages/Console/Editor/Lexer/SymbolMatch.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Explorer.Unstrip.ColorUtility;
+using ExplorerBeta;
+using UnityEngine;
+
+namespace Explorer.UI.Main.Pages.Console.Lexer
+{
+ public sealed class SymbolMatch : MatchLexer
+ {
+ public override Color HighlightColor => new Color(0.58f, 0.47f, 0.37f, 1.0f);
+
+ public string Symbols => @"[ ] ( ) . ? : + - * / % & | ^ ~ = < > ++ -- && || << >> == != <= >=
+ += -= *= /= %= &= |= ^= <<= >>= -> ?? =>";
+
+ private static readonly List shortlist = new List();
+ private static readonly Stack removeList = new Stack();
+ private string[] symbolCache = null;
+
+ public override IEnumerable StartChars
+ {
+ get
+ {
+ BuildSymbolCache();
+ foreach (string symbol in symbolCache.Where(x => x.Length > 0))
+ yield return symbol[0];
+ }
+ }
+
+ public override IEnumerable EndChars
+ {
+ get
+ {
+ BuildSymbolCache();
+ foreach (string symbol in symbolCache.Where(x => x.Length > 0))
+ yield return symbol[0];
+ }
+ }
+
+ public override bool IsImplicitMatch(ILexer 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))
+ return false;
+
+ shortlist.Clear();
+
+ int currentIndex = 0;
+ char currentChar = lexer.ReadNext();
+
+ for (int i = symbolCache.Length - 1; i >= 0; i--)
+ {
+ if (symbolCache[i][0] == currentChar)
+ shortlist.Add(symbolCache[i]);
+ }
+
+ if (shortlist.Count == 0)
+ return false;
+
+ do
+ {
+ if (lexer.EndOfStream)
+ {
+ RemoveLongStrings(currentIndex + 1);
+ break;
+ }
+
+ currentChar = lexer.ReadNext();
+ currentIndex++;
+
+ if (char.IsWhiteSpace(currentChar) ||
+ char.IsLetter(currentChar) ||
+ char.IsDigit(currentChar) ||
+ lexer.IsSpecialSymbol(currentChar, SpecialCharacterPosition.Start))
+ {
+ RemoveLongStrings(currentIndex);
+ lexer.Rollback(1);
+ break;
+ }
+
+ foreach (string symbol in shortlist)
+ {
+ if (currentIndex >= symbol.Length || symbol[currentIndex] != currentChar)
+ {
+ removeList.Push(symbol);
+ }
+ }
+
+ while (removeList.Count > 0)
+ shortlist.Remove(removeList.Pop());
+ }
+ while (shortlist.Count > 0);
+
+ return shortlist.Count > 0;
+ }
+
+ private void RemoveLongStrings(int length)
+ {
+ foreach (string keyword in shortlist)
+ {
+ if (keyword.Length > length)
+ {
+ removeList.Push(keyword);
+ }
+ }
+
+ while (removeList.Count > 0)
+ shortlist.Remove(removeList.Pop());
+ }
+
+ private void BuildSymbolCache()
+ {
+ if (symbolCache != null)
+ return;
+
+ var symSplit = Symbols.Split(' ');
+ var list = new List();
+ foreach (var sym in symSplit)
+ {
+ if (!string.IsNullOrEmpty(sym) && sym.Length > 0)
+ {
+ list.Add(sym);
+ }
+ }
+ symbolCache = list.ToArray();
+ }
+ }
+}
diff --git a/src/UI/Main/Pages/ConsolePage.cs b/src/UI/Main/Pages/ConsolePage.cs
index bd0b42e..1ea296d 100644
--- a/src/UI/Main/Pages/ConsolePage.cs
+++ b/src/UI/Main/Pages/ConsolePage.cs
@@ -21,7 +21,7 @@ namespace Explorer.UI.Main.Pages
public static ConsolePage Instance { get; private set; }
- private CodeEditor codeEditor;
+ private CodeEditor m_codeEditor;
private ScriptEvaluator m_evaluator;
@@ -62,7 +62,7 @@ namespace Explorer.UI.Main.Pages
public override void Update()
{
- codeEditor?.Update();
+ m_codeEditor?.Update();
}
internal string AsmToUsing(string asm, bool richText = false)
@@ -121,6 +121,110 @@ namespace Explorer.UI.Main.Pages
UsingDirectives = new List();
}
+ private static string m_prevInput = "NULL";
+
+ // TODO call from OnInputChanged
+
+ private void CheckAutocomplete()
+ {
+ var input = m_codeEditor.InputField.text;
+ var caretIndex = m_codeEditor.InputField.caretPosition;
+
+ if (!string.IsNullOrEmpty(input))
+ {
+ try
+ {
+ var splitChars = new[] { ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&' };
+
+ // Credit ManlyMarco
+ // Separate input into parts, grab only the part with cursor in it
+ var start = caretIndex <= 0 ? 0 : input.LastIndexOfAny(splitChars, (int)(caretIndex - 1)) + 1;
+ var end = caretIndex <= 0 ? input.Length : input.IndexOfAny(splitChars, (int)(caretIndex - 1));
+ if (end < 0 || end < start) end = input.Length;
+ input = input.Substring(start, end - start);
+ }
+ catch (ArgumentException) { }
+
+ if (!string.IsNullOrEmpty(input) && input != m_prevInput)
+ {
+ GetAutocompletes(input);
+ }
+ }
+ else
+ {
+ ClearAutocompletes();
+ }
+
+ m_prevInput = input;
+ }
+
+ private void ClearAutocompletes()
+ {
+ if (AutoCompletes.Any())
+ {
+ AutoCompletes.Clear();
+ }
+ }
+
+ private void UseAutocomplete(string suggestion)
+ {
+ int cursorIndex = m_codeEditor.InputField.caretPosition;
+ var input = m_codeEditor.InputField.text;
+ input = input.Insert(cursorIndex, suggestion);
+ m_codeEditor.InputField.text = input;
+
+ ClearAutocompletes();
+ }
+
+ private void GetAutocompletes(string input)
+ {
+ try
+ {
+ //ExplorerCore.Log("Fetching suggestions for input " + input);
+
+ // Credit ManylMarco
+ AutoCompletes.Clear();
+ var completions = m_evaluator.GetCompletions(input, out string prefix);
+ if (completions != null)
+ {
+ if (prefix == null)
+ prefix = input;
+
+ AutoCompletes.AddRange(completions
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Select(x => new AutoComplete(x, prefix, AutoComplete.Contexts.Other))
+ );
+ }
+
+ var trimmed = input.Trim();
+ if (trimmed.StartsWith("using"))
+ trimmed = trimmed.Remove(0, 5).Trim();
+
+ var namespaces = AutoCompleteHelpers.Namespaces
+ .Where(x => x.StartsWith(trimmed) && x.Length > trimmed.Length)
+ .Select(x => new AutoComplete(
+ x.Substring(trimmed.Length),
+ x.Substring(0, trimmed.Length),
+ AutoComplete.Contexts.Namespace));
+
+ AutoCompletes.AddRange(namespaces);
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.Log("C# Console error:\r\n" + ex);
+ ClearAutocompletes();
+ }
+ }
+
+ // Call on OnInputChanged, or maybe limit frequency if its too laggy
+
+ // update autocomplete buttons
+
+ private void RefreshAutocompleteButtons()
+ {
+ throw new NotImplementedException("TODO");
+ }
+
#region UI Construction
public void ConstructUI()
@@ -139,7 +243,7 @@ namespace Explorer.UI.Main.Pages
var topBarObj = UIFactory.CreateHorizontalGroup(Content);
var topBarLayout = topBarObj.AddComponent();
- topBarLayout.preferredHeight = 50;
+ topBarLayout.minHeight = 50;
topBarLayout.flexibleHeight = 0;
var topBarGroup = topBarObj.GetComponent();
@@ -348,7 +452,7 @@ namespace Explorer.UI.Main.Pages
try
{
- codeEditor = new CodeEditor(inputField, mainTextInput, highlightTextInput, linesTextInput,
+ m_codeEditor = new CodeEditor(inputField, mainTextInput, highlightTextInput, linesTextInput,
mainBgImage, lineHighlightImage, linesBgImage, scrollImage);
}
catch (Exception e)