mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-15 13:57:31 +08:00
cleanup and refactor code editor
This commit is contained in:
parent
32684bc63e
commit
2256828384
@ -256,18 +256,16 @@
|
||||
<Compile Include="UI\Main\MainMenu.cs" />
|
||||
<Compile Include="UI\Main\Pages\BaseMenuPage.cs" />
|
||||
<Compile Include="UI\Main\Pages\ConsolePage.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\AutoIndent.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\CodeEditor.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\CodeTheme.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\InputTheme.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\CommentGroupMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\CSharpLexer.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\CommentMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\ILexer.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\InputStringLexer.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\KeywordGroupMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\LiteralGroupMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\InputLexer.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\KeywordMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\StringMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\MatchLexer.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\NumberGroupMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\SymbolGroupMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\NumberMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\SymbolMatch.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\ScriptEvaluator\AutoComplete.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\ScriptEvaluator\ScriptEvaluator.cs" />
|
||||
<Compile Include="UI\Main\Pages\Console\ScriptEvaluator\ScriptInteraction.cs" />
|
||||
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Should auto indent be used for this language.
|
||||
/// </summary>
|
||||
public static bool allowAutoIndent = true;
|
||||
/// <summary>
|
||||
/// The character that causes the indent level to increase.
|
||||
/// </summary>
|
||||
public static char indentIncreaseCharacter = '{';
|
||||
/// <summary>
|
||||
/// The character that causes the indent level to decrease.
|
||||
/// </summary>
|
||||
public static char indentDecreaseCharacter = '}';
|
||||
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// Get the string representation of the indent character.
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
173
src/UI/Main/Pages/Console/Editor/CSharpLexer.cs
Normal file
173
src/UI/Main/Pages/Console/Editor/CSharpLexer.cs
Normal file
@ -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<MatchLexer> matcherList = new List<MatchLexer>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<RectTransform>();
|
||||
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<RectTransform>();
|
||||
this.lineHighlightTransform = lineHighlight.GetComponent<RectTransform>();
|
||||
|
||||
ApplyTheme();
|
||||
ApplyLanguage();
|
||||
|
||||
// subscribe to text input changing
|
||||
InputField.onValueChanged.AddListener(new Action<string>((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<RectTransform>();
|
||||
this.lineHighlightTransform = lineHighlight.GetComponent<RectTransform>();
|
||||
|
||||
// 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>((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<RectTransform>();
|
||||
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()
|
||||
|
@ -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"
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A symbol group used to specify which symbols should be highlighted.
|
||||
/// </summary>
|
||||
public static SymbolGroupMatch symbolGroup = new SymbolGroupMatch
|
||||
{
|
||||
symbols = @"[ ] ( ) . ? : + - * / % & | ^ ~ = < > ++ -- && || << >> == != <= >=
|
||||
+= -= *= /= %= &= |= ^= <<= >>= -> ?? =>",
|
||||
highlightColor = new Color(0.58f, 0.47f, 0.37f, 1.0f),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A number group used to specify whether numbers should be highlighted.
|
||||
/// </summary>
|
||||
public static NumberGroupMatch numberGroup = new NumberGroupMatch
|
||||
{
|
||||
highlightNumbers = true,
|
||||
highlightColor = new Color(0.58f, 0.33f, 0.33f, 1.0f)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A comment group used to specify which strings open and close comments.
|
||||
/// </summary>
|
||||
public static CommentGroupMatch commentGroup = new CommentGroupMatch
|
||||
{
|
||||
blockCommentEnd = @"*/",
|
||||
blockCommentStart = @"/*",
|
||||
lineCommentStart = @"//",
|
||||
lineCommentHasPresedence = true,
|
||||
highlightColor = new Color(0.34f, 0.65f, 0.29f, 1.0f),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A literal group used to specify whether quote strings should be highlighted.
|
||||
/// </summary>
|
||||
public static LiteralGroupMatch literalGroup = new LiteralGroupMatch
|
||||
{
|
||||
highlightLiterals = true,
|
||||
highlightColor = new Color(0.79f, 0.52f, 0.32f, 1.0f)
|
||||
};
|
||||
|
||||
///// <summary>
|
||||
///// Options group for all auto indent related settings.
|
||||
///// </summary>
|
||||
//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<MatchLexer> matcherList = new List<MatchLexer>
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Explorer.Unstrip.ColorUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Explorer.UI.Main.Pages.Console.Lexer
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to match line and block comments.
|
||||
/// </summary>
|
||||
public sealed class CommentGroupMatch : MatchLexer
|
||||
{
|
||||
[NonSerialized]
|
||||
private string htmlColor = null;
|
||||
|
||||
// Public
|
||||
/// <summary>
|
||||
/// The string that denotes the start of a line comment.
|
||||
/// Leave this value empty if line comments should not be highlighted.
|
||||
/// </summary>
|
||||
public string lineCommentStart;
|
||||
/// <summary>
|
||||
/// The string that denotes the start of a block comment.
|
||||
/// Leave this value empty if block comments should not be highlighted.
|
||||
/// </summary>
|
||||
public string blockCommentStart;
|
||||
/// <summary>
|
||||
/// The string that denotes the end of a block comment.
|
||||
/// </summary>
|
||||
public string blockCommentEnd;
|
||||
/// <summary>
|
||||
/// The color that comments will be highlighted with.
|
||||
/// </summary>
|
||||
public Color highlightColor = Color.black;
|
||||
|
||||
public bool lineCommentHasPresedence = true;
|
||||
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public bool HasCommentHighlighting
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.IsNullOrEmpty(lineCommentStart) == false ||
|
||||
string.IsNullOrEmpty(blockCommentStart) == false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the html tag color that comments will be highlighted with.
|
||||
/// </summary>
|
||||
public override string HTMLColor
|
||||
{
|
||||
get
|
||||
{
|
||||
// Build html color string
|
||||
if (htmlColor == null)
|
||||
htmlColor = "<#" + highlightColor.ToHex() + ">";
|
||||
|
||||
return htmlColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable collection of characters from this group that can act as delimiter symbols when they appear after a keyword.
|
||||
/// </summary>
|
||||
public override IEnumerable<char> SpecialStartCharacters
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(lineCommentStart) == false)
|
||||
yield return lineCommentStart[0];
|
||||
|
||||
if (string.IsNullOrEmpty(blockCommentEnd) == false)
|
||||
yield return blockCommentEnd[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable collection of characters from this group that can act as delimiter symbols when they appear before a keyword.
|
||||
/// </summary>
|
||||
public override IEnumerable<char> SpecialEndCharacters
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(blockCommentEnd) == false)
|
||||
yield return blockCommentEnd[blockCommentEnd.Length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
/// <summary>
|
||||
/// Causes the cached values to be reloaded.
|
||||
/// Useful for editor visualisation.
|
||||
/// </summary>
|
||||
public override void Invalidate()
|
||||
{
|
||||
this.htmlColor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the lexer input contains a valid comment format as the next character sequence.
|
||||
/// </summary>
|
||||
/// <param name="lexer">The input lexer</param>
|
||||
/// <returns>True if a comment was found or false if not</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
47
src/UI/Main/Pages/Console/Editor/Lexer/CommentMatch.cs
Normal file
47
src/UI/Main/Pages/Console/Editor/Lexer/CommentMatch.cs
Normal file
@ -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<char> StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] };
|
||||
public override IEnumerable<char> 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';
|
||||
}
|
||||
}
|
@ -6,62 +6,20 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Explorer.UI.Main.Pages.Console.Lexer
|
||||
{
|
||||
// Types
|
||||
/// <summary>
|
||||
/// Represents a keyword position where a special character may appear.
|
||||
/// </summary>
|
||||
public enum SpecialCharacterPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The special character may appear before a keyword.
|
||||
/// </summary>
|
||||
Start,
|
||||
/// <summary>
|
||||
/// The special character may appear after a keyword.
|
||||
/// </summary>
|
||||
End,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Represents a streamable lexer input which can be examined by matchers.
|
||||
/// </summary>
|
||||
public interface ILexer
|
||||
{
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// Returns true if there are no more characters to read.
|
||||
/// </summary>
|
||||
bool EndOfStream { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the previously read character or '\0' if there is no previous character.
|
||||
/// </summary>
|
||||
char Previous { get; }
|
||||
|
||||
// Methods
|
||||
/// <summary>
|
||||
/// Attempt to read the next character.
|
||||
/// </summary>
|
||||
/// <returns>The next character in the stream or '\0' if the end of stream is reached</returns>
|
||||
char ReadNext();
|
||||
|
||||
/// <summary>
|
||||
/// Causes the lexer to return its state to a previously commited state.
|
||||
/// </summary>
|
||||
/// <param name="amount">Use -1 to return to the last commited state or a positive number to represent the number of characters to rollback</param>
|
||||
void Rollback(int amount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Causes all read characters to be commited meaning that rollback will return to this lexer state.
|
||||
/// </summary>
|
||||
void Commit();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified character is considered a special symbol by the lexer meaning that it is able to act as a delimiter.
|
||||
/// </summary>
|
||||
/// <param name="character">The character to check</param>
|
||||
/// <param name="position">The character position to check. This determines whether the character may appear at the start or end of a keyword</param>
|
||||
/// <returns>True if the character is a valid delimiter or false if not</returns>
|
||||
bool IsSpecialSymbol(char character, SpecialCharacterPosition position = SpecialCharacterPosition.Start);
|
||||
}
|
||||
}
|
||||
|
@ -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<char> specialStartSymbols = new HashSet<char>();
|
||||
@ -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<InputStringMatchInfo> LexInputString(string input)
|
||||
public IEnumerable<LexerMatchInfo> 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();
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A matcher that checks for a number of predefined keywords in the lexer stream.
|
||||
/// </summary>
|
||||
public sealed class KeywordGroupMatch : MatchLexer
|
||||
{
|
||||
// Private
|
||||
private static readonly HashSet<string> shortlist = new HashSet<string>();
|
||||
private static readonly Stack<string> removeList = new Stack<string>();
|
||||
private string[] keywordCache = null;
|
||||
private string htmlColor = null;
|
||||
|
||||
// Public
|
||||
/// <summary>
|
||||
/// Used for editor gui only. Has no purpose other than to give the inspector foldout a nice name
|
||||
/// </summary>
|
||||
public string group = "Keyword Group"; // This value is not used - it just gives the inspector foldout a nice name
|
||||
/// <summary>
|
||||
/// A string containing one or more keywords separated by a space character that will be used by this matcher.
|
||||
/// </summary>
|
||||
public string keywords;
|
||||
/// <summary>
|
||||
/// The color that any matched keywords will be highlighted.
|
||||
/// </summary>
|
||||
public Color highlightColor = Color.black;
|
||||
/// <summary>
|
||||
/// Should keyword matching be case sensitive.
|
||||
/// </summary>
|
||||
public bool caseSensitive = true;
|
||||
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// Get a value indicating whether keyword highlighting is enabled based upon the number of valid keywords found.
|
||||
/// </summary>
|
||||
public bool HasKeywordHighlighting
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check for valid keyword
|
||||
if (string.IsNullOrEmpty(keywords) == false)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the html formatted color tag that any matched keywords will be highlighted with.
|
||||
/// </summary>
|
||||
public override string HTMLColor
|
||||
{
|
||||
get
|
||||
{
|
||||
// Get html color
|
||||
if (htmlColor == null)
|
||||
htmlColor = "<#" + highlightColor.ToHex() + ">";
|
||||
|
||||
return htmlColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
/// <summary>
|
||||
/// Causes any cached data to be reloaded.
|
||||
/// </summary>
|
||||
public override void Invalidate()
|
||||
{
|
||||
this.htmlColor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the specified lexer has a valid keyword at its current position.
|
||||
/// </summary>
|
||||
/// <param name="lexer">The input lexer to check</param>
|
||||
/// <returns>True if the stream has a keyword or false if not</returns>
|
||||
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<string>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
110
src/UI/Main/Pages/Console/Editor/Lexer/KeywordMatch.cs
Normal file
110
src/UI/Main/Pages/Console/Editor/Lexer/KeywordMatch.cs
Normal file
@ -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<string> shortlist = new HashSet<string>();
|
||||
private readonly Stack<string> removeList = new Stack<string>();
|
||||
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<string>();
|
||||
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));
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A matcher that checks for quote strings in the lexer stream.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class LiteralGroupMatch : MatchLexer
|
||||
{
|
||||
// Private
|
||||
private string htmlColor = null;
|
||||
|
||||
// Public
|
||||
/// <summary>
|
||||
/// Should literal be highlighted.
|
||||
/// When true, any text surrounded by double quotes will be highlighted.
|
||||
/// </summary>
|
||||
public bool highlightLiterals = true;
|
||||
/// <summary>
|
||||
/// The color that any matched literals will be highlighted.
|
||||
/// </summary>
|
||||
public Color highlightColor = Color.black;
|
||||
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// Get a value indicating whether literal highlighting is enabled.
|
||||
/// </summary>
|
||||
public bool HasLiteralHighlighting
|
||||
{
|
||||
get { return highlightLiterals; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the html formatted color tag that any matched literals will be highlighted with.
|
||||
/// </summary>
|
||||
public override string HTMLColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (htmlColor == null)
|
||||
htmlColor = "<#" + highlightColor.ToHex() + ">";
|
||||
|
||||
return htmlColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns special symbols that can act as delimiters when appearing before a word.
|
||||
/// In this case '"' will be returned.
|
||||
/// </summary>
|
||||
public override IEnumerable<char> SpecialStartCharacters
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return '"';
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns special symbols that can act as delimiters when appearing after a word.
|
||||
/// In this case '"' will be returned.
|
||||
/// </summary>
|
||||
public override IEnumerable<char> SpecialEndCharacters
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return '"';
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
/// <summary>
|
||||
/// Causes any cached data to be reloaded.
|
||||
/// </summary>
|
||||
public override void Invalidate()
|
||||
{
|
||||
this.htmlColor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the specified lexer has a valid literal at its current position.
|
||||
/// </summary>
|
||||
/// <param name="lexer">The input lexer to check</param>
|
||||
/// <returns>True if the stream has a literal or false if not</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the html formatted color tag that any matched text will be highlighted with.
|
||||
/// </summary>
|
||||
public abstract string HTMLColor { get; }
|
||||
public abstract Color HighlightColor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get an enumerable collection of special characters that can act as delimiter symbols when they appear before a word.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<char> SpecialStartCharacters { get { yield break; } }
|
||||
public string HexColor => htmlColor ?? (htmlColor = "<#" + HighlightColor.ToHex() + ">");
|
||||
private string htmlColor = null;
|
||||
|
||||
/// <summary>
|
||||
/// Get an enumerable collection of special characters that can act as delimiter symbols when they appear after a word.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<char> SpecialEndCharacters { get { yield break; } }
|
||||
public virtual IEnumerable<char> StartChars { get { yield break; } }
|
||||
public virtual IEnumerable<char> EndChars { get { yield break; } }
|
||||
|
||||
// Methods
|
||||
/// <summary>
|
||||
/// Checks the specified lexers current position for a certain sequence of characters as defined by the inheriting matcher.
|
||||
/// </summary>
|
||||
/// <param name="lexer"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool IsImplicitMatch(ILexer lexer);
|
||||
|
||||
/// <summary>
|
||||
/// Causes the matcher to invalidate any cached data forcing it to be regenerated or reloaded.
|
||||
/// </summary>
|
||||
public virtual void Invalidate() { }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to check for a match in the specified lexer.
|
||||
/// </summary>
|
||||
/// <param name="lexer">The lexer that will be checked</param>
|
||||
/// <returns>True if a match was found or false if not</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A matcher that checks for any numbers that appear in the lexer stream.
|
||||
/// </summary>
|
||||
public sealed class NumberGroupMatch : MatchLexer
|
||||
{
|
||||
// Private
|
||||
private string htmlColor = null;
|
||||
|
||||
// Public
|
||||
/// <summary>
|
||||
/// Should number highlighting be used.
|
||||
/// When false, numbers will appear in the default text color as defined by the current editor theme.
|
||||
/// </summary>
|
||||
public bool highlightNumbers = true;
|
||||
/// <summary>
|
||||
/// The color that any matched numbers will be highlighted.
|
||||
/// </summary>
|
||||
public Color highlightColor = Color.black;
|
||||
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// Get a value indicating whether keyword highlighting is enabled.
|
||||
/// </summary>
|
||||
public bool HasNumberHighlighting
|
||||
{
|
||||
get { return highlightNumbers; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the html formatted color tag that any matched numbers will be highlighted with.
|
||||
/// </summary>
|
||||
public override string HTMLColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (htmlColor == null)
|
||||
htmlColor = "<#" + highlightColor.ToHex() + ">";
|
||||
|
||||
return htmlColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
/// <summary>
|
||||
/// Causes any cached data to be reloaded.
|
||||
/// </summary>
|
||||
public override void Invalidate()
|
||||
{
|
||||
this.htmlColor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the specified lexer has a valid number sequence at its current position.
|
||||
/// </summary>
|
||||
/// <param name="lexer">The input lexer to check</param>
|
||||
/// <returns>True if the stream has a number sequence or false if not</returns>
|
||||
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 == '.';
|
||||
}
|
||||
|
||||
}
|
44
src/UI/Main/Pages/Console/Editor/Lexer/NumberMatch.cs
Normal file
44
src/UI/Main/Pages/Console/Editor/Lexer/NumberMatch.cs
Normal file
@ -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 == '.';
|
||||
}
|
||||
|
||||
}
|
37
src/UI/Main/Pages/Console/Editor/Lexer/StringMatch.cs
Normal file
37
src/UI/Main/Pages/Console/Editor/Lexer/StringMatch.cs
Normal file
@ -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<char> StartChars { get { yield return '"'; } }
|
||||
public override IEnumerable<char> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A matcher that checks for a number of predefined symbols in the lexer stream.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class SymbolGroupMatch : MatchLexer
|
||||
{
|
||||
// Private
|
||||
private static readonly List<string> shortlist = new List<string>();
|
||||
private static readonly Stack<string> removeList = new Stack<string>();
|
||||
[NonSerialized]
|
||||
private string[] symbolCache = null;
|
||||
[NonSerialized]
|
||||
private string htmlColor = null;
|
||||
|
||||
// Public
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string symbols;
|
||||
/// <summary>
|
||||
/// The color that any matched symbols will be highlighted.
|
||||
/// </summary>
|
||||
public Color highlightColor = Color.black;
|
||||
|
||||
// Properties
|
||||
/// <summary>
|
||||
/// Get a value indicating whether symbol highlighting is enabled based upon the number of valid symbols found.
|
||||
/// </summary>
|
||||
public bool HasSymbolHighlighting
|
||||
{
|
||||
get { return symbols.Length > 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the html formatted color tag that any matched symbols will be highlighted with.
|
||||
/// </summary>
|
||||
public override string HTMLColor
|
||||
{
|
||||
get
|
||||
{
|
||||
// Get html color
|
||||
if (htmlColor == null)
|
||||
htmlColor = "<#" + highlightColor.ToHex() + ">";
|
||||
|
||||
return htmlColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns special symbols that can act as delimiters when appearing before a word.
|
||||
/// </summary>
|
||||
public override IEnumerable<char> 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];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns special symbols that can act as delimiters when appearing after a word.
|
||||
/// In this case '"' will be returned.
|
||||
/// </summary>
|
||||
public override IEnumerable<char> 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
|
||||
/// <summary>
|
||||
/// Causes any cached data to be reloaded.
|
||||
/// </summary>
|
||||
public override void Invalidate()
|
||||
{
|
||||
this.htmlColor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the specified lexer has a valid symbol at its current posiiton.
|
||||
/// </summary>
|
||||
/// <param name="lexer">The input lexer to check</param>
|
||||
/// <returns>True if the stream has a symbol or false if not</returns>
|
||||
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<string>();
|
||||
foreach (var sym in symSplit)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sym) && sym.Length > 0)
|
||||
{
|
||||
list.Add(sym);
|
||||
}
|
||||
}
|
||||
symbolCache = list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
137
src/UI/Main/Pages/Console/Editor/Lexer/SymbolMatch.cs
Normal file
137
src/UI/Main/Pages/Console/Editor/Lexer/SymbolMatch.cs
Normal file
@ -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<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> 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<string>();
|
||||
foreach (var sym in symSplit)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sym) && sym.Length > 0)
|
||||
{
|
||||
list.Add(sym);
|
||||
}
|
||||
}
|
||||
symbolCache = list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string>();
|
||||
}
|
||||
|
||||
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<LayoutElement>();
|
||||
topBarLayout.preferredHeight = 50;
|
||||
topBarLayout.minHeight = 50;
|
||||
topBarLayout.flexibleHeight = 0;
|
||||
|
||||
var topBarGroup = topBarObj.GetComponent<HorizontalLayoutGroup>();
|
||||
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user