mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 14:17:51 +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\MainMenu.cs" />
|
||||||
<Compile Include="UI\Main\Pages\BaseMenuPage.cs" />
|
<Compile Include="UI\Main\Pages\BaseMenuPage.cs" />
|
||||||
<Compile Include="UI\Main\Pages\ConsolePage.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\CodeEditor.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\CodeTheme.cs" />
|
<Compile Include="UI\Main\Pages\Console\Editor\CSharpLexer.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\InputTheme.cs" />
|
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\CommentMatch.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\CommentGroupMatch.cs" />
|
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\ILexer.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\InputLexer.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\KeywordGroupMatch.cs" />
|
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\KeywordMatch.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\LiteralGroupMatch.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\MatchLexer.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\NumberGroupMatch.cs" />
|
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\NumberMatch.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\Editor\Lexer\SymbolGroupMatch.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\AutoComplete.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\ScriptEvaluator\ScriptEvaluator.cs" />
|
<Compile Include="UI\Main\Pages\Console\ScriptEvaluator\ScriptEvaluator.cs" />
|
||||||
<Compile Include="UI\Main\Pages\Console\ScriptEvaluator\ScriptInteraction.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 ExplorerBeta.Input;
|
||||||
using Explorer.UI.Main.Pages.Console.Lexer;
|
using Explorer.UI.Main.Pages.Console.Lexer;
|
||||||
using ExplorerBeta;
|
using ExplorerBeta;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Explorer.UI.Main.Pages.Console
|
namespace Explorer.UI.Main.Pages.Console
|
||||||
{
|
{
|
||||||
public class CodeEditor
|
public class CodeEditor
|
||||||
{
|
{
|
||||||
public CodeEditor(TMP_InputField inputField, TextMeshProUGUI inputText, TextMeshProUGUI inputHighlightText, TextMeshProUGUI lineText,
|
private readonly InputLexer inputLexer = new InputLexer();
|
||||||
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;
|
|
||||||
|
|
||||||
var highlightTextRect = inputHighlightText.GetComponent<RectTransform>();
|
public TMP_InputField InputField { get; }
|
||||||
highlightTextRect.anchorMin = Vector2.zero;
|
|
||||||
highlightTextRect.anchorMax = Vector2.one;
|
|
||||||
highlightTextRect.offsetMin = Vector2.zero;
|
|
||||||
highlightTextRect.offsetMax = Vector2.zero;
|
|
||||||
|
|
||||||
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 inputText;
|
||||||
private readonly TextMeshProUGUI inputHighlightText;
|
private readonly TextMeshProUGUI inputHighlightText;
|
||||||
private readonly TextMeshProUGUI lineText;
|
private readonly TextMeshProUGUI lineText;
|
||||||
@ -69,13 +25,26 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
private readonly Image lineNumberBackground;
|
private readonly Image lineNumberBackground;
|
||||||
private readonly Image scrollbar;
|
private readonly Image scrollbar;
|
||||||
|
|
||||||
private bool lineNumbers = true;
|
private string lastText;
|
||||||
private int lineNumbersSize = 20;
|
private bool lineHighlightLocked;
|
||||||
|
private readonly RectTransform inputTextTransform;
|
||||||
|
private readonly RectTransform lineHighlightTransform;
|
||||||
|
|
||||||
public int LineCount { get; private set; } = 0;
|
public int LineCount { get; private set; }
|
||||||
public int CurrentLine { get; private set; } = 0;
|
public int CurrentLine { get; private set; }
|
||||||
public int CurrentColumn { get; private set; } = 0;
|
public int CurrentColumn { get; private set; }
|
||||||
public int CurrentIndent { get; private set; } = 0;
|
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
|
public string Text
|
||||||
{
|
{
|
||||||
@ -97,65 +66,40 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string HighlightedText => inputHighlightText.text;
|
public CodeEditor(TMP_InputField inputField, TextMeshProUGUI inputText, TextMeshProUGUI inputHighlightText, TextMeshProUGUI lineText,
|
||||||
|
Image background, Image lineHighlight, Image lineNumberBackground, Image scrollbar)
|
||||||
public bool LineNumbers
|
|
||||||
{
|
{
|
||||||
get { return lineNumbers; }
|
this.InputField = inputField;
|
||||||
set
|
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;
|
throw new Exception("References are missing!");
|
||||||
|
|
||||||
//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);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo maybe not needed
|
this.inputTextTransform = inputText.GetComponent<RectTransform>();
|
||||||
public int LineNumbersSize
|
this.lineHighlightTransform = lineHighlight.GetComponent<RectTransform>();
|
||||||
{
|
|
||||||
get { return lineNumbersSize; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
lineNumbersSize = value;
|
|
||||||
|
|
||||||
// Update the line numbers
|
ApplyTheme();
|
||||||
LineNumbers = lineNumbers;
|
inputLexer.UseMatchers(CSharpLexer.DelimiterSymbols, CSharpLexer.Matchers);
|
||||||
}
|
|
||||||
|
// subscribe to text input changing
|
||||||
|
this.InputField.onValueChanged.AddListener(new Action<string>((string s) => { OnInputChanged(); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
|
||||||
// Auto indent
|
|
||||||
if (AutoIndent.autoIndentMode != AutoIndent.IndentMode.None)
|
|
||||||
{
|
{
|
||||||
// Check for new line
|
// Check for new line
|
||||||
if (InputManager.GetKeyDown(KeyCode.Return))
|
if (InputManager.GetKeyDown(KeyCode.Return))
|
||||||
{
|
{
|
||||||
AutoIndentCaret();
|
AutoIndentCaret();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool focusKeyPressed = false;
|
bool focusKeyPressed = false;
|
||||||
|
|
||||||
@ -172,54 +116,16 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
// Update line highlight
|
// Update line highlight
|
||||||
if (focusKeyPressed || InputManager.GetMouseButton(0))
|
if (focusKeyPressed || InputManager.GetMouseButton(0))
|
||||||
{
|
{
|
||||||
UpdateCurrentLineHighlight();
|
UpdateHighlight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Refresh(bool forceUpdate = false)
|
public void OnInputChanged(bool forceUpdate = false)
|
||||||
{
|
{
|
||||||
// Trigger a content change event
|
var newText = InputField.text;
|
||||||
DisplayedContentChanged(InputField.text, forceUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetLineHighlight(int lineNumber, bool lockLineHighlight)
|
UpdateIndent();
|
||||||
{
|
|
||||||
// Check if code editor is not active
|
|
||||||
if (lineNumber < 1 || lineNumber > LineCount)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//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 ((!forceUpdate && lastText == newText) || string.IsNullOrEmpty(newText))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(newText))
|
if (string.IsNullOrEmpty(newText))
|
||||||
@ -227,37 +133,45 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
inputHighlightText.text = string.Empty;
|
inputHighlightText.text = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Its possible the text was cleared so we need to sync numbers and highlighter
|
UpdateLineNumbers();
|
||||||
UpdateCurrentLineNumbers();
|
UpdateHighlight();
|
||||||
UpdateCurrentLineHighlight();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputHighlightText.text = SyntaxHighlightContent(newText);
|
inputHighlightText.text = SyntaxHighlightContent(newText);
|
||||||
|
|
||||||
// Sync line numbers and update the line highlight
|
// Sync line numbers and update the line highlight
|
||||||
UpdateCurrentLineNumbers();
|
UpdateLineNumbers();
|
||||||
UpdateCurrentLineHighlight();
|
UpdateHighlight();
|
||||||
|
|
||||||
this.lastText = newText;
|
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 currentLineCount = inputText.textInfo.lineCount;
|
||||||
|
|
||||||
int currentLineNumber = 1;
|
int currentLineNumber = 1;
|
||||||
|
|
||||||
// Check for a change in line
|
|
||||||
if (currentLineCount != LineCount)
|
if (currentLineCount != LineCount)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Update line numbers
|
|
||||||
lineBuilder.Length = 0;
|
lineBuilder.Length = 0;
|
||||||
|
|
||||||
// Build line numbers string
|
|
||||||
for (int i = 1; i < currentLineCount + 2; i++)
|
for (int i = 1; i < currentLineCount + 2; i++)
|
||||||
{
|
{
|
||||||
if (i - 1 > 0 && i - 1 < currentLineCount - 1)
|
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();
|
lineText.text = lineBuilder.ToString();
|
||||||
LineCount = currentLineCount;
|
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;
|
int caret = InputField.caretPosition;
|
||||||
|
|
||||||
if (caret < 0 || caret >= inputText.textInfo.characterInfo.Count)
|
if (caret < 0 || caret >= inputText.textInfo.characterInfo.Count)
|
||||||
@ -318,48 +230,35 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
|
|
||||||
CurrentLine = inputText.textInfo.characterInfo[caret].lineNumber;
|
CurrentLine = inputText.textInfo.characterInfo[caret].lineNumber;
|
||||||
|
|
||||||
// Get the total character count
|
|
||||||
int charCount = 0;
|
int charCount = 0;
|
||||||
for (int i = 0; i < CurrentLine; i++)
|
for (int i = 0; i < CurrentLine; i++)
|
||||||
charCount += inputText.textInfo.lineInfo[i].characterCount;
|
charCount += inputText.textInfo.lineInfo[i].characterCount;
|
||||||
|
|
||||||
// Get the column position
|
|
||||||
CurrentColumn = caret - charCount;
|
CurrentColumn = caret - charCount;
|
||||||
|
|
||||||
CurrentIndent = 0;
|
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 == CSharpLexer.indentIncreaseCharacter)
|
||||||
if (character == AutoIndent.indentIncreaseCharacter)
|
|
||||||
CurrentIndent++;
|
CurrentIndent++;
|
||||||
|
|
||||||
// Check for closing indents
|
if (character == CSharpLexer.indentDecreaseCharacter)
|
||||||
if (character == AutoIndent.indentDecreaseCharacter)
|
|
||||||
CurrentIndent--;
|
CurrentIndent--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dont allow negative indents
|
|
||||||
if (CurrentIndent < 0)
|
if (CurrentIndent < 0)
|
||||||
CurrentIndent = 0;
|
CurrentIndent = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateCurrentLineHighlight()
|
private void UpdateHighlight()
|
||||||
{
|
{
|
||||||
if (lineHighlightLocked)
|
if (lineHighlightLocked)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// unity 2018.2 and older may need lineOffset as 0? not sure
|
|
||||||
//int lineOffset = 1;
|
|
||||||
|
|
||||||
int caret = InputField.caretPosition - 1;
|
int caret = InputField.caretPosition - 1;
|
||||||
|
|
||||||
var lineHeight = inputText.textInfo.lineInfo[inputText.textInfo.characterInfo[0].lineNumber].lineHeight;
|
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)
|
private string SyntaxHighlightContent(string inputText)
|
||||||
{
|
{
|
||||||
if (!InputTheme.allowSyntaxHighlighting)
|
|
||||||
return inputText;
|
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
highlightedBuilder.Length = 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++)
|
for (int i = offset; i < match.startIndex; i++)
|
||||||
highlightedBuilder.Append(inputText[i]);
|
highlightedBuilder.Append(inputText[i]);
|
||||||
|
|
||||||
// Add the opening color tag
|
|
||||||
highlightedBuilder.Append(match.htmlColor);
|
highlightedBuilder.Append(match.htmlColor);
|
||||||
|
|
||||||
// Copy text inbetween the match boundaries
|
|
||||||
for (int i = match.startIndex; i < match.endIndex; i++)
|
for (int i = match.startIndex; i < match.endIndex; i++)
|
||||||
highlightedBuilder.Append(inputText[i]);
|
highlightedBuilder.Append(inputText[i]);
|
||||||
|
|
||||||
// Add the closing color tag
|
|
||||||
highlightedBuilder.Append(CLOSE_COLOR_TAG);
|
highlightedBuilder.Append(CLOSE_COLOR_TAG);
|
||||||
|
|
||||||
// Update offset
|
|
||||||
offset = match.endIndex;
|
offset = match.endIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy remaining text
|
|
||||||
for (int i = offset; i < inputText.Length; i++)
|
for (int i = offset; i < inputText.Length; i++)
|
||||||
highlightedBuilder.Append(inputText[i]);
|
highlightedBuilder.Append(inputText[i]);
|
||||||
|
|
||||||
// Convert to string
|
|
||||||
inputText = highlightedBuilder.ToString();
|
inputText = highlightedBuilder.ToString();
|
||||||
|
|
||||||
return inputText;
|
return inputText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo param is probably pointless
|
|
||||||
private void AutoIndentCaret()
|
private void AutoIndentCaret()
|
||||||
{
|
{
|
||||||
if (CurrentIndent > 0)
|
if (CurrentIndent > 0)
|
||||||
@ -429,7 +317,7 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
var indentMinusOne = indent.Substring(0, indent.Length - 1);
|
var indentMinusOne = indent.Substring(0, indent.Length - 1);
|
||||||
|
|
||||||
// get last index of {
|
// 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 text = InputField.text;
|
||||||
var sub = InputField.text.Substring(0, InputField.caretPosition);
|
var sub = InputField.text.Substring(0, InputField.caretPosition);
|
||||||
var lastIndex = sub.LastIndexOf("{");
|
var lastIndex = sub.LastIndexOf("{");
|
||||||
@ -444,37 +332,9 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if should add auto-close }
|
// check if should add auto-close }
|
||||||
int numOpen = 0;
|
int numOpen = InputField.text.Where(x => x == CSharpLexer.indentIncreaseCharacter).Count();
|
||||||
int numClose = 0;
|
int numClose = InputField.text.Where(x => x == CSharpLexer.indentDecreaseCharacter).Count();
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (numOpen > numClose)
|
if (numOpen > numClose)
|
||||||
{
|
{
|
||||||
// add auto-indent closing
|
// add auto-indent closing
|
||||||
@ -490,14 +350,14 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update line column and indent positions
|
// Update line column and indent positions
|
||||||
UpdateCurrentLineColumnIndent();
|
UpdateIndent();
|
||||||
|
|
||||||
inputText.text = InputField.text;
|
inputText.text = InputField.text;
|
||||||
inputText.SetText(InputField.text, true);
|
inputText.SetText(InputField.text, true);
|
||||||
inputText.Rebuild(CanvasUpdate.Prelayout);
|
inputText.Rebuild(CanvasUpdate.Prelayout);
|
||||||
InputField.ForceLabelUpdate();
|
InputField.ForceLabelUpdate();
|
||||||
InputField.Rebuild(CanvasUpdate.Prelayout);
|
InputField.Rebuild(CanvasUpdate.Prelayout);
|
||||||
Refresh(true);
|
OnInputChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetAutoIndentTab(int amount)
|
private string GetAutoIndentTab(int amount)
|
||||||
@ -510,26 +370,34 @@ namespace Explorer.UI.Main.Pages.Console
|
|||||||
return tab;
|
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()
|
private void ApplyTheme()
|
||||||
{
|
{
|
||||||
// Check for missing references
|
var highlightTextRect = inputHighlightText.GetComponent<RectTransform>();
|
||||||
if (!AllReferencesAssigned())
|
highlightTextRect.anchorMin = Vector2.zero;
|
||||||
throw new Exception("Cannot apply theme because one or more required component references are missing. ");
|
highlightTextRect.anchorMax = Vector2.one;
|
||||||
|
highlightTextRect.offsetMin = Vector2.zero;
|
||||||
|
highlightTextRect.offsetMax = Vector2.zero;
|
||||||
|
|
||||||
// Apply theme colors
|
InputField.caretColor = caretColor;
|
||||||
InputField.caretColor = InputTheme.caretColor;
|
inputText.color = textColor;
|
||||||
inputText.color = InputTheme.textColor;
|
inputHighlightText.color = textColor;
|
||||||
inputHighlightText.color = InputTheme.textColor;
|
background.color = backgroundColor;
|
||||||
background.color = InputTheme.backgroundColor;
|
lineHighlight.color = lineHighlightColor;
|
||||||
lineHighlight.color = InputTheme.lineHighlightColor;
|
lineNumberBackground.color = lineNumberBackgroundColor;
|
||||||
lineNumberBackground.color = InputTheme.lineNumberBackgroundColor;
|
lineText.color = lineNumberTextColor;
|
||||||
lineText.color = InputTheme.lineNumberTextColor;
|
scrollbar.color = scrollbarColor;
|
||||||
scrollbar.color = InputTheme.scrollbarColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyLanguage()
|
private void ApplyLanguage()
|
||||||
{
|
{
|
||||||
lexer.UseMatchers(CodeTheme.DelimiterSymbols, CodeTheme.Matchers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AllReferencesAssigned()
|
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
|
namespace Explorer.UI.Main.Pages.Console.Lexer
|
||||||
{
|
{
|
||||||
// Types
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a keyword position where a special character may appear.
|
|
||||||
/// </summary>
|
|
||||||
public enum SpecialCharacterPosition
|
public enum SpecialCharacterPosition
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The special character may appear before a keyword.
|
|
||||||
/// </summary>
|
|
||||||
Start,
|
Start,
|
||||||
/// <summary>
|
|
||||||
/// The special character may appear after a keyword.
|
|
||||||
/// </summary>
|
|
||||||
End,
|
End,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a streamable lexer input which can be examined by matchers.
|
|
||||||
/// </summary>
|
|
||||||
public interface ILexer
|
public interface ILexer
|
||||||
{
|
{
|
||||||
// Properties
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if there are no more characters to read.
|
|
||||||
/// </summary>
|
|
||||||
bool EndOfStream { get; }
|
bool EndOfStream { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the previously read character or '\0' if there is no previous character.
|
|
||||||
/// </summary>
|
|
||||||
char Previous { get; }
|
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();
|
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);
|
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();
|
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);
|
bool IsSpecialSymbol(char character, SpecialCharacterPosition position = SpecialCharacterPosition.Start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,15 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Explorer.UI.Main.Pages.Console.Lexer
|
namespace Explorer.UI.Main.Pages.Console.Lexer
|
||||||
{
|
{
|
||||||
internal struct InputStringMatchInfo
|
internal struct LexerMatchInfo
|
||||||
{
|
{
|
||||||
// Public
|
|
||||||
public int startIndex;
|
public int startIndex;
|
||||||
public int endIndex;
|
public int endIndex;
|
||||||
public string htmlColor;
|
public string htmlColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class InputStringLexer : ILexer
|
internal class InputLexer : ILexer
|
||||||
{
|
{
|
||||||
// Private
|
|
||||||
private string inputString = null;
|
private string inputString = null;
|
||||||
private MatchLexer[] matchers = null;
|
private MatchLexer[] matchers = null;
|
||||||
private readonly HashSet<char> specialStartSymbols = new HashSet<char>();
|
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 currentIndex = 0;
|
||||||
private int currentLookaheadIndex = 0;
|
private int currentLookaheadIndex = 0;
|
||||||
|
|
||||||
// Properties
|
|
||||||
public bool EndOfStream
|
public bool EndOfStream
|
||||||
{
|
{
|
||||||
get { return currentLookaheadIndex >= inputString.Length; }
|
get { return currentLookaheadIndex >= inputString.Length; }
|
||||||
@ -37,102 +34,82 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
|
|||||||
get { return previous; }
|
get { return previous; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods
|
|
||||||
public void UseMatchers(char[] delimiters, MatchLexer[] matchers)
|
public void UseMatchers(char[] delimiters, MatchLexer[] matchers)
|
||||||
{
|
{
|
||||||
// Store matchers
|
|
||||||
this.matchers = matchers;
|
this.matchers = matchers;
|
||||||
|
|
||||||
// Clear old symbols
|
|
||||||
specialStartSymbols.Clear();
|
specialStartSymbols.Clear();
|
||||||
specialEndSymbols.Clear();
|
specialEndSymbols.Clear();
|
||||||
|
|
||||||
// Check for any delimiter characters
|
|
||||||
if (delimiters != null)
|
if (delimiters != null)
|
||||||
{
|
{
|
||||||
// Add delimiters
|
|
||||||
foreach (char character in delimiters)
|
foreach (char character in delimiters)
|
||||||
{
|
{
|
||||||
// Add to start
|
|
||||||
if (specialStartSymbols.Contains(character) == false)
|
if (specialStartSymbols.Contains(character) == false)
|
||||||
specialStartSymbols.Add(character);
|
specialStartSymbols.Add(character);
|
||||||
|
|
||||||
// Add to end
|
|
||||||
if (specialEndSymbols.Contains(character) == false)
|
if (specialEndSymbols.Contains(character) == false)
|
||||||
specialEndSymbols.Add(character);
|
specialEndSymbols.Add(character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for any matchers
|
|
||||||
if (matchers != null)
|
if (matchers != null)
|
||||||
{
|
{
|
||||||
// Add all special symbols which can act as a delimiter
|
|
||||||
foreach (MatchLexer lexer in matchers)
|
foreach (MatchLexer lexer in matchers)
|
||||||
{
|
{
|
||||||
foreach (char special in lexer.SpecialStartCharacters)
|
foreach (char special in lexer.StartChars)
|
||||||
if (specialStartSymbols.Contains(special) == false)
|
if (specialStartSymbols.Contains(special) == false)
|
||||||
specialStartSymbols.Add(special);
|
specialStartSymbols.Add(special);
|
||||||
|
|
||||||
foreach (char special in lexer.SpecialEndCharacters)
|
foreach (char special in lexer.EndChars)
|
||||||
if (specialEndSymbols.Contains(special) == false)
|
if (specialEndSymbols.Contains(special) == false)
|
||||||
specialEndSymbols.Add(special);
|
specialEndSymbols.Add(special);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<InputStringMatchInfo> LexInputString(string input)
|
public IEnumerable<LexerMatchInfo> LexInputString(string input)
|
||||||
{
|
{
|
||||||
if (input == null || matchers == null || matchers.Length == 0)
|
if (input == null || matchers == null || matchers.Length == 0)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
// Store the input string
|
|
||||||
this.inputString = input;
|
this.inputString = input;
|
||||||
this.current = ' ';
|
this.current = ' ';
|
||||||
this.previous = ' ';
|
this.previous = ' ';
|
||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
this.currentLookaheadIndex = 0;
|
this.currentLookaheadIndex = 0;
|
||||||
|
|
||||||
// Process the input string
|
|
||||||
while (EndOfStream == false)
|
while (EndOfStream == false)
|
||||||
{
|
{
|
||||||
bool didMatchLexer = false;
|
bool didMatchLexer = false;
|
||||||
|
|
||||||
// Read any white spaces
|
|
||||||
ReadWhiteSpace();
|
ReadWhiteSpace();
|
||||||
|
|
||||||
// Process each matcher
|
|
||||||
foreach (MatchLexer matcher in matchers)
|
foreach (MatchLexer matcher in matchers)
|
||||||
{
|
{
|
||||||
// Get the current index
|
|
||||||
int startIndex = currentIndex;
|
int startIndex = currentIndex;
|
||||||
|
|
||||||
// Try to match
|
|
||||||
bool isMatched = matcher.IsMatch(this);
|
bool isMatched = matcher.IsMatch(this);
|
||||||
|
|
||||||
if (isMatched == true)
|
if (isMatched == true)
|
||||||
{
|
{
|
||||||
// Get the end index of the match
|
|
||||||
int endIndex = currentIndex;
|
int endIndex = currentIndex;
|
||||||
|
|
||||||
// Set matched flag
|
|
||||||
didMatchLexer = true;
|
didMatchLexer = true;
|
||||||
|
|
||||||
// Register the match
|
yield return new LexerMatchInfo
|
||||||
yield return new InputStringMatchInfo
|
|
||||||
{
|
{
|
||||||
startIndex = startIndex,
|
startIndex = startIndex,
|
||||||
endIndex = endIndex,
|
endIndex = endIndex,
|
||||||
htmlColor = matcher.HTMLColor,
|
htmlColor = matcher.HexColor,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Move to next character
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (didMatchLexer == false)
|
if (didMatchLexer == false)
|
||||||
{
|
{
|
||||||
// Move to next
|
|
||||||
ReadNext();
|
ReadNext();
|
||||||
Commit();
|
Commit();
|
||||||
}
|
}
|
||||||
@ -141,14 +118,11 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
|
|||||||
|
|
||||||
public char ReadNext()
|
public char ReadNext()
|
||||||
{
|
{
|
||||||
// Check for end of stream
|
|
||||||
if (EndOfStream == true)
|
if (EndOfStream == true)
|
||||||
return '\0';
|
return '\0';
|
||||||
|
|
||||||
// Update previous character
|
|
||||||
previous = current;
|
previous = current;
|
||||||
|
|
||||||
// Get the character
|
|
||||||
current = inputString[currentLookaheadIndex];
|
current = inputString[currentLookaheadIndex];
|
||||||
currentLookaheadIndex++;
|
currentLookaheadIndex++;
|
||||||
|
|
||||||
@ -159,7 +133,6 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
|
|||||||
{
|
{
|
||||||
if (amount == -1)
|
if (amount == -1)
|
||||||
{
|
{
|
||||||
// Revert to index
|
|
||||||
currentLookaheadIndex = currentIndex;
|
currentLookaheadIndex = currentIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -193,14 +166,11 @@ namespace Explorer.UI.Main.Pages.Console.Lexer
|
|||||||
|
|
||||||
private void ReadWhiteSpace()
|
private void ReadWhiteSpace()
|
||||||
{
|
{
|
||||||
// Read until white space
|
|
||||||
while (char.IsWhiteSpace(ReadNext()) == true)
|
while (char.IsWhiteSpace(ReadNext()) == true)
|
||||||
{
|
{
|
||||||
// Consume the char
|
|
||||||
Commit();
|
Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return lexer state
|
|
||||||
Rollback();
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Explorer.Unstrip.ColorUtility;
|
||||||
|
using ExplorerBeta;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Explorer.UI.Main.Pages.Console.Lexer
|
namespace Explorer.UI.Main.Pages.Console.Lexer
|
||||||
{
|
{
|
||||||
public abstract class MatchLexer
|
public abstract class MatchLexer
|
||||||
{
|
{
|
||||||
/// <summary>
|
public abstract Color HighlightColor { get; }
|
||||||
/// Get the html formatted color tag that any matched text will be highlighted with.
|
|
||||||
/// </summary>
|
|
||||||
public abstract string HTMLColor { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
public string HexColor => htmlColor ?? (htmlColor = "<#" + HighlightColor.ToHex() + ">");
|
||||||
/// Get an enumerable collection of special characters that can act as delimiter symbols when they appear before a word.
|
private string htmlColor = null;
|
||||||
/// </summary>
|
|
||||||
public virtual IEnumerable<char> SpecialStartCharacters { get { yield break; } }
|
|
||||||
|
|
||||||
/// <summary>
|
public virtual IEnumerable<char> StartChars { get { yield break; } }
|
||||||
/// Get an enumerable collection of special characters that can act as delimiter symbols when they appear after a word.
|
public virtual IEnumerable<char> EndChars { get { yield break; } }
|
||||||
/// </summary>
|
|
||||||
public virtual IEnumerable<char> SpecialEndCharacters { 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);
|
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)
|
public bool IsMatch(ILexer lexer)
|
||||||
{
|
{
|
||||||
// Check for implicit match
|
if (IsImplicitMatch(lexer))
|
||||||
bool match = IsImplicitMatch(lexer);
|
|
||||||
|
|
||||||
if (match == true)
|
|
||||||
{
|
{
|
||||||
// Consume read tokens
|
|
||||||
lexer.Commit();
|
lexer.Commit();
|
||||||
}
|
return true;
|
||||||
else
|
|
||||||
{
|
|
||||||
// Revert lexer state
|
|
||||||
lexer.Rollback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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; }
|
public static ConsolePage Instance { get; private set; }
|
||||||
|
|
||||||
private CodeEditor codeEditor;
|
private CodeEditor m_codeEditor;
|
||||||
|
|
||||||
private ScriptEvaluator m_evaluator;
|
private ScriptEvaluator m_evaluator;
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ namespace Explorer.UI.Main.Pages
|
|||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
codeEditor?.Update();
|
m_codeEditor?.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string AsmToUsing(string asm, bool richText = false)
|
internal string AsmToUsing(string asm, bool richText = false)
|
||||||
@ -121,6 +121,110 @@ namespace Explorer.UI.Main.Pages
|
|||||||
UsingDirectives = new List<string>();
|
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
|
#region UI Construction
|
||||||
|
|
||||||
public void ConstructUI()
|
public void ConstructUI()
|
||||||
@ -139,7 +243,7 @@ namespace Explorer.UI.Main.Pages
|
|||||||
|
|
||||||
var topBarObj = UIFactory.CreateHorizontalGroup(Content);
|
var topBarObj = UIFactory.CreateHorizontalGroup(Content);
|
||||||
var topBarLayout = topBarObj.AddComponent<LayoutElement>();
|
var topBarLayout = topBarObj.AddComponent<LayoutElement>();
|
||||||
topBarLayout.preferredHeight = 50;
|
topBarLayout.minHeight = 50;
|
||||||
topBarLayout.flexibleHeight = 0;
|
topBarLayout.flexibleHeight = 0;
|
||||||
|
|
||||||
var topBarGroup = topBarObj.GetComponent<HorizontalLayoutGroup>();
|
var topBarGroup = topBarObj.GetComponent<HorizontalLayoutGroup>();
|
||||||
@ -348,7 +452,7 @@ namespace Explorer.UI.Main.Pages
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
codeEditor = new CodeEditor(inputField, mainTextInput, highlightTextInput, linesTextInput,
|
m_codeEditor = new CodeEditor(inputField, mainTextInput, highlightTextInput, linesTextInput,
|
||||||
mainBgImage, lineHighlightImage, linesBgImage, scrollImage);
|
mainBgImage, lineHighlightImage, linesBgImage, scrollImage);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user