mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-18 17:17:52 +08:00
Cleanup and simplify highlight process, reduce string alloc
This commit is contained in:
@ -1,9 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core.CSharp;
|
using UnityExplorer.Core.CSharp;
|
||||||
using UnityExplorer.Core.Input;
|
using UnityExplorer.Core.Input;
|
||||||
@ -72,20 +74,20 @@ The following helper methods are available:
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Lexer = new LexerBuilder();
|
|
||||||
|
|
||||||
ResetConsole(false);
|
ResetConsole(false);
|
||||||
Evaluator.Compile("0 == 0");
|
Evaluator.Compile("0 == 0");
|
||||||
|
|
||||||
Panel.OnInputChanged += OnConsoleInputChanged;
|
|
||||||
Panel.InputScroll.OnScroll += ForceOnContentChange;
|
|
||||||
// TODO other panel listeners (buttons, etc)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning(ex);
|
ExplorerCore.LogWarning("C# Console probably not supported, todo");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Lexer = new LexerBuilder();
|
||||||
|
|
||||||
|
Panel.OnInputChanged += OnConsoleInputChanged;
|
||||||
|
Panel.InputScroll.OnScroll += ForceOnContentChange;
|
||||||
|
// TODO other panel listeners (buttons, etc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updating and event listeners
|
// Updating and event listeners
|
||||||
@ -98,6 +100,8 @@ The following helper methods are available:
|
|||||||
|
|
||||||
public static void Update()
|
public static void Update()
|
||||||
{
|
{
|
||||||
|
UpdateCaret();
|
||||||
|
|
||||||
if (EnableCtrlRShortcut)
|
if (EnableCtrlRShortcut)
|
||||||
{
|
{
|
||||||
if ((InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
if ((InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||||
@ -131,80 +135,21 @@ The following helper methods are available:
|
|||||||
private static void OnConsoleInputChanged(string value)
|
private static void OnConsoleInputChanged(string value)
|
||||||
{
|
{
|
||||||
// todo auto indent? or only on enter?
|
// todo auto indent? or only on enter?
|
||||||
|
// todo update auto completes
|
||||||
|
|
||||||
|
|
||||||
// syntax highlight
|
// syntax highlight
|
||||||
LexerHighlightAndSet(value);
|
HighlightVisibleInput(value);
|
||||||
|
|
||||||
|
|
||||||
// todo update auto completes
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LexerHighlightAndSet(string value)
|
private static void UpdateCaret()
|
||||||
{
|
{
|
||||||
int startLine = 0;
|
LastCaretPosition = Input.InputField.caretPosition;
|
||||||
int endLine = Input.TextGenerator.lineCount - 1;
|
|
||||||
|
|
||||||
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
|
// todo check if out of bounds
|
||||||
{
|
|
||||||
// Not all text is displayed.
|
|
||||||
// Only syntax highlight what we need to.
|
|
||||||
|
|
||||||
int topLine = -1;
|
|
||||||
int bottomLine = -1;
|
|
||||||
var half = Input.Rect.rect.height * 0.5f;
|
|
||||||
|
|
||||||
var top = Input.Rect.rect.height - Input.Rect.anchoredPosition.y;
|
|
||||||
var bottom = top - Panel.InputScroll.ViewportRect.rect.height;
|
|
||||||
|
|
||||||
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
|
|
||||||
{
|
|
||||||
var line = Input.TextGenerator.lines[i];
|
|
||||||
var pos = line.topY + half;
|
|
||||||
|
|
||||||
if (topLine == -1 && pos <= top)
|
|
||||||
topLine = i;
|
|
||||||
|
|
||||||
if ((pos - line.height) >= bottom)
|
|
||||||
bottomLine = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
startLine = Math.Max(0, topLine - 1);
|
|
||||||
endLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int startIdx = Input.TextGenerator.lines[startLine].startCharIdx;
|
|
||||||
int endIdx;
|
|
||||||
if (endLine >= Input.TextGenerator.lineCount - 1)
|
|
||||||
endIdx = value.Length - 1;
|
|
||||||
else
|
|
||||||
endIdx = Math.Min(value.Length - 1, Input.TextGenerator.lines[endLine + 1].startCharIdx);
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
for (int i = 0; i < startLine; i++)
|
|
||||||
sb.Append('\n');
|
|
||||||
for (int i = startIdx; i <= endIdx; i++)
|
|
||||||
sb.Append(value[i]);
|
|
||||||
|
|
||||||
Panel.HighlightText.text = Lexer.SyntaxHighlight(sb.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO indenting
|
|
||||||
|
|
||||||
//private static void DoAutoIndent()
|
|
||||||
//{
|
|
||||||
// int caret = Panel.LastCaretPosition;
|
|
||||||
// Panel.InputField.Text = Lexer.AutoIndentOnEnter(InputField.text, ref caret);
|
|
||||||
// InputField.caretPosition = caret;
|
|
||||||
//
|
|
||||||
// Panel.InputText.Rebuild(CanvasUpdate.Prelayout);
|
|
||||||
// InputField.ForceLabelUpdate();
|
|
||||||
// InputField.Rebuild(CanvasUpdate.Prelayout);
|
|
||||||
//
|
|
||||||
// OnConsoleInputChanged(InputField.text);
|
|
||||||
//}
|
|
||||||
|
|
||||||
#region Evaluating console input
|
#region Evaluating console input
|
||||||
|
|
||||||
public static void ResetConsole(bool logSuccess = true)
|
public static void ResetConsole(bool logSuccess = true)
|
||||||
@ -267,5 +212,278 @@ The following helper methods are available:
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Lexer Highlighting
|
||||||
|
|
||||||
|
private static void HighlightVisibleInput(string value)
|
||||||
|
{
|
||||||
|
int startLine = 0;
|
||||||
|
int endLine = Input.TextGenerator.lineCount - 1;
|
||||||
|
|
||||||
|
// Calculate visible text if necessary
|
||||||
|
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
|
||||||
|
{
|
||||||
|
// This was mostly done through trial and error, it probably depends on the anchoring.
|
||||||
|
int topLine = -1;
|
||||||
|
int bottomLine = -1;
|
||||||
|
var heightCorrection = Input.Rect.rect.height * 0.5f;
|
||||||
|
|
||||||
|
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y;
|
||||||
|
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
|
||||||
|
|
||||||
|
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
|
||||||
|
{
|
||||||
|
var line = Input.TextGenerator.lines[i];
|
||||||
|
var pos = line.topY + heightCorrection;
|
||||||
|
|
||||||
|
// if top of line is below the viewport top
|
||||||
|
if (topLine == -1 && pos <= viewportMin)
|
||||||
|
topLine = i;
|
||||||
|
|
||||||
|
// if bottom of line is below the viewport bottom
|
||||||
|
if ((pos - line.height) >= viewportMax)
|
||||||
|
bottomLine = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
startLine = Math.Max(0, topLine - 1);
|
||||||
|
endLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIdx = Input.TextGenerator.lines[startLine].startCharIdx;
|
||||||
|
int endIdx;
|
||||||
|
if (endLine >= Input.TextGenerator.lineCount - 1)
|
||||||
|
endIdx = value.Length - 1;
|
||||||
|
else
|
||||||
|
endIdx = Math.Min(value.Length - 1, Input.TextGenerator.lines[endLine + 1].startCharIdx);
|
||||||
|
|
||||||
|
|
||||||
|
// Highlight the visible text with the LexerBuilder
|
||||||
|
Panel.HighlightText.text = Lexer.BuildHighlightedString(value, startIdx, endIdx, startLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Autocompletes
|
||||||
|
|
||||||
|
public static void UseSuggestion(string suggestion)
|
||||||
|
{
|
||||||
|
string input = Input.Text;
|
||||||
|
input = input.Insert(LastCaretPosition, suggestion);
|
||||||
|
Input.Text = input;
|
||||||
|
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaret(LastCaretPosition += suggestion.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int LastCaretPosition { get; private set; }
|
||||||
|
internal static float defaultInputFieldAlpha;
|
||||||
|
|
||||||
|
private static IEnumerator SetAutocompleteCaret(int caretPosition)
|
||||||
|
{
|
||||||
|
var color = Input.InputField.selectionColor;
|
||||||
|
color.a = 0f;
|
||||||
|
Input.InputField.selectionColor = color;
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
EventSystem.current.SetSelectedGameObject(Panel.Input.UIRoot, null);
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
Input.InputField.caretPosition = caretPosition;
|
||||||
|
Input.InputField.selectionFocusPosition = caretPosition;
|
||||||
|
color.a = defaultInputFieldAlpha;
|
||||||
|
Input.InputField.selectionColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
// TODO indenting
|
||||||
|
#region AUTO INDENT TODO
|
||||||
|
|
||||||
|
//private static void DoAutoIndent()
|
||||||
|
//{
|
||||||
|
// int caret = Panel.LastCaretPosition;
|
||||||
|
// Panel.InputField.Text = Lexer.AutoIndentOnEnter(InputField.text, ref caret);
|
||||||
|
// InputField.caretPosition = caret;
|
||||||
|
//
|
||||||
|
// Panel.InputText.Rebuild(CanvasUpdate.Prelayout);
|
||||||
|
// InputField.ForceLabelUpdate();
|
||||||
|
// InputField.Rebuild(CanvasUpdate.Prelayout);
|
||||||
|
//
|
||||||
|
// OnConsoleInputChanged(InputField.text);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
// Auto-indenting
|
||||||
|
|
||||||
|
//public int GetIndentLevel(string input, int toIndex)
|
||||||
|
//{
|
||||||
|
// bool stringState = false;
|
||||||
|
// int indent = 0;
|
||||||
|
//
|
||||||
|
// for (int i = 0; i < toIndex && i < input.Length; i++)
|
||||||
|
// {
|
||||||
|
// char character = input[i];
|
||||||
|
//
|
||||||
|
// if (character == '"')
|
||||||
|
// stringState = !stringState;
|
||||||
|
// else if (!stringState && character == INDENT_OPEN)
|
||||||
|
// indent++;
|
||||||
|
// else if (!stringState && character == INDENT_CLOSE)
|
||||||
|
// indent--;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (indent < 0)
|
||||||
|
// indent = 0;
|
||||||
|
//
|
||||||
|
// return indent;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// TODO not quite correct, but almost there.
|
||||||
|
//
|
||||||
|
//public string AutoIndentOnEnter(string input, ref int caretPos)
|
||||||
|
//{
|
||||||
|
// var sb = new StringBuilder(input);
|
||||||
|
//
|
||||||
|
// bool inString = false;
|
||||||
|
// bool inChar = false;
|
||||||
|
// int currentIndent = 0;
|
||||||
|
// int curLineIndent = 0;
|
||||||
|
// bool prevWasNewLine = true;
|
||||||
|
//
|
||||||
|
// // process before caret position
|
||||||
|
// for (int i = 0; i < caretPos; i++)
|
||||||
|
// {
|
||||||
|
// char c = sb[i];
|
||||||
|
//
|
||||||
|
// ExplorerCore.Log(i + ": " + c);
|
||||||
|
//
|
||||||
|
// // update string/char state
|
||||||
|
// if (!inChar && c == '\"')
|
||||||
|
// inString = !inString;
|
||||||
|
// else if (!inString && c == '\'')
|
||||||
|
// inChar = !inChar;
|
||||||
|
//
|
||||||
|
// // continue if inside string or char
|
||||||
|
// if (inString || inChar)
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// // check for new line
|
||||||
|
// if (c == '\n')
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("new line, resetting line counts");
|
||||||
|
// curLineIndent = 0;
|
||||||
|
// prevWasNewLine = true;
|
||||||
|
// }
|
||||||
|
// // check for indent
|
||||||
|
// else if (c == '\t' && prevWasNewLine)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("its a tab");
|
||||||
|
// if (curLineIndent > currentIndent)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("too many tabs, removing");
|
||||||
|
// // already reached the indent we should have
|
||||||
|
// sb.Remove(i, 1);
|
||||||
|
// i--;
|
||||||
|
// caretPos--;
|
||||||
|
// curLineIndent--;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// curLineIndent++;
|
||||||
|
// }
|
||||||
|
// // remove spaces on new lines
|
||||||
|
// else if (c == ' ' && prevWasNewLine)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("removing newline-space");
|
||||||
|
// sb.Remove(i, 1);
|
||||||
|
// i--;
|
||||||
|
// caretPos--;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (c == INDENT_CLOSE)
|
||||||
|
// currentIndent--;
|
||||||
|
//
|
||||||
|
// if (prevWasNewLine && curLineIndent < currentIndent)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("line is not indented enough");
|
||||||
|
// // line is not indented enough
|
||||||
|
// int diff = currentIndent - curLineIndent;
|
||||||
|
// sb.Insert(i, new string('\t', diff));
|
||||||
|
// caretPos += diff;
|
||||||
|
// i += diff;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // check for brackets
|
||||||
|
// if ((c == INDENT_CLOSE || c == INDENT_OPEN) && !prevWasNewLine)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("bracket needs new line");
|
||||||
|
//
|
||||||
|
// // need to put it on a new line
|
||||||
|
// sb.Insert(i, $"\n{new string('\t', currentIndent)}");
|
||||||
|
// caretPos += 1 + currentIndent;
|
||||||
|
// i += 1 + currentIndent;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (c == INDENT_OPEN)
|
||||||
|
// currentIndent++;
|
||||||
|
//
|
||||||
|
// prevWasNewLine = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // todo put caret on new line after previous bracket if needed
|
||||||
|
// // indent caret to current indent
|
||||||
|
//
|
||||||
|
// // process after caret position, make sure there are equal opened/closed brackets
|
||||||
|
// ExplorerCore.Log("-- after caret --");
|
||||||
|
// for (int i = caretPos; i < sb.Length; i++)
|
||||||
|
// {
|
||||||
|
// char c = sb[i];
|
||||||
|
// ExplorerCore.Log(i + ": " + c);
|
||||||
|
//
|
||||||
|
// // update string/char state
|
||||||
|
// if (!inChar && c == '\"')
|
||||||
|
// inString = !inString;
|
||||||
|
// else if (!inString && c == '\'')
|
||||||
|
// inChar = !inChar;
|
||||||
|
//
|
||||||
|
// if (inString || inChar)
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// if (c == INDENT_OPEN)
|
||||||
|
// currentIndent++;
|
||||||
|
// else if (c == INDENT_CLOSE)
|
||||||
|
// currentIndent--;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (currentIndent > 0)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("there are not enough closing brackets, curIndent is " + currentIndent);
|
||||||
|
// // There are not enough close brackets
|
||||||
|
//
|
||||||
|
// // TODO this should append in reverse indent order (small indents inserted first, then biggest).
|
||||||
|
// while (currentIndent > 0)
|
||||||
|
// {
|
||||||
|
// ExplorerCore.Log("Inserting closing bracket with " + currentIndent + " indent");
|
||||||
|
// // append the indented '}' on a new line
|
||||||
|
// sb.Insert(caretPos, $"\n{new string('\t', currentIndent - 1)}}}");
|
||||||
|
//
|
||||||
|
// currentIndent--;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// //else if (currentIndent < 0)
|
||||||
|
// //{
|
||||||
|
// // // There are too many close brackets
|
||||||
|
// //
|
||||||
|
// // // todo?
|
||||||
|
// //}
|
||||||
|
//
|
||||||
|
// return sb.ToString();
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
public class LexerBuilder
|
public class LexerBuilder
|
||||||
{
|
{
|
||||||
// Initialization and core
|
#region Core and initialization
|
||||||
|
|
||||||
public const char WHITESPACE = ' ';
|
public const char WHITESPACE = ' ';
|
||||||
public const char INDENT_OPEN = '{';
|
public const char INDENT_OPEN = '{';
|
||||||
@ -47,35 +47,45 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDelimiter(char character, bool orWhitespace = false, bool orLetterOrDigit = false)
|
#endregion
|
||||||
{
|
|
||||||
return delimiters.Contains(character)
|
|
||||||
|| (orWhitespace && char.IsWhiteSpace(character))
|
|
||||||
|| (orLetterOrDigit && char.IsLetterOrDigit(character));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lexer enumeration
|
|
||||||
|
|
||||||
public string InputString { get; private set; }
|
|
||||||
public int Length => InputString.Length;
|
|
||||||
|
|
||||||
public int LastCommittedIndex { get; private set; }
|
public int LastCommittedIndex { get; private set; }
|
||||||
public int LookaheadIndex { get; private set; }
|
public int LookaheadIndex { get; private set; }
|
||||||
|
|
||||||
public char Current => !EndOfInput ? InputString[LookaheadIndex] : WHITESPACE;
|
public char Current => !EndOfInput ? currentInput[LookaheadIndex] : WHITESPACE;
|
||||||
public char Previous => LookaheadIndex >= 1 ? InputString[LookaheadIndex - 1] : WHITESPACE;
|
public char Previous => LookaheadIndex >= 1 ? currentInput[LookaheadIndex - 1] : WHITESPACE;
|
||||||
|
|
||||||
public bool EndOfInput => LookaheadIndex >= Length;
|
public bool EndOfInput => LookaheadIndex > currentEndIdx;
|
||||||
public bool EndOrNewLine => EndOfInput || Current == '\n' || Current == '\r';
|
public bool EndOrNewLine => EndOfInput || Current == '\n' || Current == '\r';
|
||||||
|
|
||||||
public string SyntaxHighlight(string input)
|
private string currentInput;
|
||||||
|
private int currentStartIdx;
|
||||||
|
private int currentEndIdx;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the range of the string with the Lexer and build a RichText-highlighted representation of it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The entire input string which you want to parse a section (or all) of</param>
|
||||||
|
/// <param name="startIdx">The first character you want to highlight</param>
|
||||||
|
/// <param name="endIdx">The last character you want to highlight</param>
|
||||||
|
/// <param name="leadingLines">The amount of leading empty lines you want before the first character in the return string.</param>
|
||||||
|
/// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns>
|
||||||
|
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
|
||||||
|
return input;
|
||||||
|
|
||||||
|
currentInput = input;
|
||||||
|
currentStartIdx = startIdx;
|
||||||
|
currentEndIdx = endIdx;
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
int lastUnhighlighted = 0;
|
|
||||||
|
|
||||||
// TODO auto indent as part of this parse
|
for (int i = 0; i < leadingLines; i++)
|
||||||
|
sb.Append('\n');
|
||||||
|
|
||||||
foreach (var match in GetMatches(input))
|
int lastUnhighlighted = startIdx;
|
||||||
|
foreach (var match in GetMatches())
|
||||||
{
|
{
|
||||||
// append non-highlighted text between last match and this
|
// append non-highlighted text between last match and this
|
||||||
for (int i = lastUnhighlighted; i < match.startIndex; i++)
|
for (int i = lastUnhighlighted; i < match.startIndex; i++)
|
||||||
@ -84,7 +94,7 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
// append the highlighted match
|
// append the highlighted match
|
||||||
sb.Append(match.htmlColorTag);
|
sb.Append(match.htmlColorTag);
|
||||||
|
|
||||||
for (int i = match.startIndex; i <= match.endIndex && i < input.Length; i++)
|
for (int i = match.startIndex; i <= match.endIndex && i <= currentEndIdx; i++)
|
||||||
sb.Append(input[i]);
|
sb.Append(input[i]);
|
||||||
|
|
||||||
sb.Append(SignatureHighlighter.CLOSE_COLOR);
|
sb.Append(SignatureHighlighter.CLOSE_COLOR);
|
||||||
@ -96,13 +106,12 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<MatchInfo> GetMatches(string input)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
InputString = input;
|
// Match builder, iterates through each Lexer and returns all matches found.
|
||||||
LastCommittedIndex = -1;
|
|
||||||
|
private IEnumerable<MatchInfo> GetMatches()
|
||||||
|
{
|
||||||
|
LastCommittedIndex = currentStartIdx - 1;
|
||||||
Rollback();
|
Rollback();
|
||||||
|
|
||||||
while (!EndOfInput)
|
while (!EndOfInput)
|
||||||
@ -137,6 +146,8 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Methods used by the Lexers for interfacing with the current parse process
|
||||||
|
|
||||||
public char PeekNext(int amount = 1)
|
public char PeekNext(int amount = 1)
|
||||||
{
|
{
|
||||||
LookaheadIndex += amount;
|
LookaheadIndex += amount;
|
||||||
@ -145,7 +156,7 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
public void Commit()
|
public void Commit()
|
||||||
{
|
{
|
||||||
LastCommittedIndex = Math.Min(Length - 1, LookaheadIndex);
|
LastCommittedIndex = Math.Min(currentEndIdx, LookaheadIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Rollback()
|
public void Rollback()
|
||||||
@ -158,6 +169,13 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
LookaheadIndex = Math.Max(LastCommittedIndex + 1, LookaheadIndex - amount);
|
LookaheadIndex = Math.Max(LastCommittedIndex + 1, LookaheadIndex - amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsDelimiter(char character, bool orWhitespace = false, bool orLetterOrDigit = false)
|
||||||
|
{
|
||||||
|
return delimiters.Contains(character)
|
||||||
|
|| (orWhitespace && char.IsWhiteSpace(character))
|
||||||
|
|| (orLetterOrDigit && char.IsLetterOrDigit(character));
|
||||||
|
}
|
||||||
|
|
||||||
private void SkipWhitespace()
|
private void SkipWhitespace()
|
||||||
{
|
{
|
||||||
// peek and commit as long as there is whitespace
|
// peek and commit as long as there is whitespace
|
||||||
@ -170,178 +188,5 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
// revert the last PeekNext which would have returned false
|
// revert the last PeekNext which would have returned false
|
||||||
Rollback();
|
Rollback();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region AUTO INDENT TODO
|
|
||||||
|
|
||||||
// Auto-indenting
|
|
||||||
|
|
||||||
//public int GetIndentLevel(string input, int toIndex)
|
|
||||||
//{
|
|
||||||
// bool stringState = false;
|
|
||||||
// int indent = 0;
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < toIndex && i < input.Length; i++)
|
|
||||||
// {
|
|
||||||
// char character = input[i];
|
|
||||||
//
|
|
||||||
// if (character == '"')
|
|
||||||
// stringState = !stringState;
|
|
||||||
// else if (!stringState && character == INDENT_OPEN)
|
|
||||||
// indent++;
|
|
||||||
// else if (!stringState && character == INDENT_CLOSE)
|
|
||||||
// indent--;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (indent < 0)
|
|
||||||
// indent = 0;
|
|
||||||
//
|
|
||||||
// return indent;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// TODO not quite correct, but almost there.
|
|
||||||
//
|
|
||||||
//public string AutoIndentOnEnter(string input, ref int caretPos)
|
|
||||||
//{
|
|
||||||
// var sb = new StringBuilder(input);
|
|
||||||
//
|
|
||||||
// bool inString = false;
|
|
||||||
// bool inChar = false;
|
|
||||||
// int currentIndent = 0;
|
|
||||||
// int curLineIndent = 0;
|
|
||||||
// bool prevWasNewLine = true;
|
|
||||||
//
|
|
||||||
// // process before caret position
|
|
||||||
// for (int i = 0; i < caretPos; i++)
|
|
||||||
// {
|
|
||||||
// char c = sb[i];
|
|
||||||
//
|
|
||||||
// ExplorerCore.Log(i + ": " + c);
|
|
||||||
//
|
|
||||||
// // update string/char state
|
|
||||||
// if (!inChar && c == '\"')
|
|
||||||
// inString = !inString;
|
|
||||||
// else if (!inString && c == '\'')
|
|
||||||
// inChar = !inChar;
|
|
||||||
//
|
|
||||||
// // continue if inside string or char
|
|
||||||
// if (inString || inChar)
|
|
||||||
// continue;
|
|
||||||
//
|
|
||||||
// // check for new line
|
|
||||||
// if (c == '\n')
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("new line, resetting line counts");
|
|
||||||
// curLineIndent = 0;
|
|
||||||
// prevWasNewLine = true;
|
|
||||||
// }
|
|
||||||
// // check for indent
|
|
||||||
// else if (c == '\t' && prevWasNewLine)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("its a tab");
|
|
||||||
// if (curLineIndent > currentIndent)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("too many tabs, removing");
|
|
||||||
// // already reached the indent we should have
|
|
||||||
// sb.Remove(i, 1);
|
|
||||||
// i--;
|
|
||||||
// caretPos--;
|
|
||||||
// curLineIndent--;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// curLineIndent++;
|
|
||||||
// }
|
|
||||||
// // remove spaces on new lines
|
|
||||||
// else if (c == ' ' && prevWasNewLine)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("removing newline-space");
|
|
||||||
// sb.Remove(i, 1);
|
|
||||||
// i--;
|
|
||||||
// caretPos--;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (c == INDENT_CLOSE)
|
|
||||||
// currentIndent--;
|
|
||||||
//
|
|
||||||
// if (prevWasNewLine && curLineIndent < currentIndent)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("line is not indented enough");
|
|
||||||
// // line is not indented enough
|
|
||||||
// int diff = currentIndent - curLineIndent;
|
|
||||||
// sb.Insert(i, new string('\t', diff));
|
|
||||||
// caretPos += diff;
|
|
||||||
// i += diff;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // check for brackets
|
|
||||||
// if ((c == INDENT_CLOSE || c == INDENT_OPEN) && !prevWasNewLine)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("bracket needs new line");
|
|
||||||
//
|
|
||||||
// // need to put it on a new line
|
|
||||||
// sb.Insert(i, $"\n{new string('\t', currentIndent)}");
|
|
||||||
// caretPos += 1 + currentIndent;
|
|
||||||
// i += 1 + currentIndent;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (c == INDENT_OPEN)
|
|
||||||
// currentIndent++;
|
|
||||||
//
|
|
||||||
// prevWasNewLine = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // todo put caret on new line after previous bracket if needed
|
|
||||||
// // indent caret to current indent
|
|
||||||
//
|
|
||||||
// // process after caret position, make sure there are equal opened/closed brackets
|
|
||||||
// ExplorerCore.Log("-- after caret --");
|
|
||||||
// for (int i = caretPos; i < sb.Length; i++)
|
|
||||||
// {
|
|
||||||
// char c = sb[i];
|
|
||||||
// ExplorerCore.Log(i + ": " + c);
|
|
||||||
//
|
|
||||||
// // update string/char state
|
|
||||||
// if (!inChar && c == '\"')
|
|
||||||
// inString = !inString;
|
|
||||||
// else if (!inString && c == '\'')
|
|
||||||
// inChar = !inChar;
|
|
||||||
//
|
|
||||||
// if (inString || inChar)
|
|
||||||
// continue;
|
|
||||||
//
|
|
||||||
// if (c == INDENT_OPEN)
|
|
||||||
// currentIndent++;
|
|
||||||
// else if (c == INDENT_CLOSE)
|
|
||||||
// currentIndent--;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (currentIndent > 0)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("there are not enough closing brackets, curIndent is " + currentIndent);
|
|
||||||
// // There are not enough close brackets
|
|
||||||
//
|
|
||||||
// // TODO this should append in reverse indent order (small indents inserted first, then biggest).
|
|
||||||
// while (currentIndent > 0)
|
|
||||||
// {
|
|
||||||
// ExplorerCore.Log("Inserting closing bracket with " + currentIndent + " indent");
|
|
||||||
// // append the indented '}' on a new line
|
|
||||||
// sb.Insert(caretPos, $"\n{new string('\t', currentIndent - 1)}}}");
|
|
||||||
//
|
|
||||||
// currentIndent--;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// //else if (currentIndent < 0)
|
|
||||||
// //{
|
|
||||||
// // // There are too many close brackets
|
|
||||||
// //
|
|
||||||
// // // todo?
|
|
||||||
// //}
|
|
||||||
//
|
|
||||||
// return sb.ToString();
|
|
||||||
//}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ namespace UnityExplorer.UI
|
|||||||
set => InputField.text = value;
|
set => InputField.text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ReachedMaxVerts => TextGenerator.vertexCount >= UIManager.MAX_TEXT_VERTS;
|
|
||||||
public TextGenerator TextGenerator => InputField.cachedInputTextGenerator;
|
public TextGenerator TextGenerator => InputField.cachedInputTextGenerator;
|
||||||
|
public bool ReachedMaxVerts => TextGenerator.vertexCount >= UIManager.MAX_TEXT_VERTS;
|
||||||
|
|
||||||
private bool updatedWanted;
|
private bool updatedWanted;
|
||||||
|
|
||||||
@ -44,7 +44,6 @@ namespace UnityExplorer.UI
|
|||||||
{
|
{
|
||||||
if (updatedWanted)
|
if (updatedWanted)
|
||||||
{
|
{
|
||||||
//LayoutRebuilder.ForceRebuildLayoutImmediate(Rect);
|
|
||||||
LayoutRebuilder.MarkLayoutForRebuild(Rect);
|
LayoutRebuilder.MarkLayoutForRebuild(Rect);
|
||||||
|
|
||||||
OnValueChanged?.Invoke(InputField.text);
|
OnValueChanged?.Invoke(InputField.text);
|
||||||
|
@ -31,24 +31,6 @@ namespace UnityExplorer.UI.Panels
|
|||||||
public Action<bool> OnSuggestionsToggled;
|
public Action<bool> OnSuggestionsToggled;
|
||||||
public Action<bool> OnAutoIndentToggled;
|
public Action<bool> OnAutoIndentToggled;
|
||||||
|
|
||||||
public int LastCaretPosition { get; private set; }
|
|
||||||
private int m_desiredCaretFix;
|
|
||||||
private bool m_fixWaiting;
|
|
||||||
private float m_defaultInputFieldAlpha;
|
|
||||||
|
|
||||||
public void UseSuggestion(string suggestion)
|
|
||||||
{
|
|
||||||
string input = Input.Text;
|
|
||||||
input = input.Insert(LastCaretPosition, suggestion);
|
|
||||||
Input.Text = input;
|
|
||||||
|
|
||||||
m_desiredCaretFix = LastCaretPosition += suggestion.Length;
|
|
||||||
|
|
||||||
var color = Input.InputField.selectionColor;
|
|
||||||
color.a = 0f;
|
|
||||||
Input.InputField.selectionColor = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InvokeOnValueChanged(string value)
|
private void InvokeOnValueChanged(string value)
|
||||||
{
|
{
|
||||||
// Todo show a label instead of just logging
|
// Todo show a label instead of just logging
|
||||||
@ -63,28 +45,6 @@ namespace UnityExplorer.UI.Panels
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
CSConsole.Update();
|
CSConsole.Update();
|
||||||
|
|
||||||
if (m_desiredCaretFix >= 0)
|
|
||||||
{
|
|
||||||
if (!m_fixWaiting)
|
|
||||||
{
|
|
||||||
EventSystem.current.SetSelectedGameObject(InputScroll.UIRoot, null);
|
|
||||||
m_fixWaiting = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Input.InputField.caretPosition = m_desiredCaretFix;
|
|
||||||
Input.InputField.selectionFocusPosition = m_desiredCaretFix;
|
|
||||||
var color = Input.InputField.selectionColor;
|
|
||||||
color.a = m_defaultInputFieldAlpha;
|
|
||||||
Input.InputField.selectionColor = color;
|
|
||||||
|
|
||||||
m_fixWaiting = false;
|
|
||||||
m_desiredCaretFix = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Input.InputField.caretPosition > 0)
|
|
||||||
LastCaretPosition = Input.InputField.caretPosition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saving
|
// Saving
|
||||||
@ -153,30 +113,33 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsole.STARTUP_TEXT, out var inputScroller, fontSize);
|
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsole.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||||
InputScroll = inputScroller;
|
InputScroll = inputScroller;
|
||||||
m_defaultInputFieldAlpha = Input.InputField.selectionColor.a;
|
CSConsole.defaultInputFieldAlpha = Input.InputField.selectionColor.a;
|
||||||
Input.OnValueChanged += InvokeOnValueChanged;
|
Input.OnValueChanged += InvokeOnValueChanged;
|
||||||
|
|
||||||
var placeHolderText = Input.PlaceholderText;
|
|
||||||
placeHolderText.fontSize = fontSize;
|
|
||||||
|
|
||||||
InputText = Input.InputField.textComponent;
|
InputText = Input.InputField.textComponent;
|
||||||
InputText.supportRichText = false;
|
InputText.supportRichText = false;
|
||||||
InputText.color = new Color(1, 1, 1, 0.65f);
|
InputText.color = new Color(1, 1, 1, 0.65f);
|
||||||
|
Input.PlaceholderText.fontSize = fontSize;
|
||||||
|
|
||||||
var mainTextObj = InputText.gameObject;
|
// Lexer highlight text overlay
|
||||||
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", mainTextObj.gameObject);
|
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", InputText.gameObject);
|
||||||
var highlightTextRect = highlightTextObj.GetComponent<RectTransform>();
|
var highlightTextRect = highlightTextObj.GetComponent<RectTransform>();
|
||||||
highlightTextRect.pivot = new Vector2(0, 1);
|
highlightTextRect.pivot = new Vector2(0, 1);
|
||||||
highlightTextRect.anchorMin = Vector2.zero;
|
highlightTextRect.anchorMin = Vector2.zero;
|
||||||
highlightTextRect.anchorMax = Vector2.one;
|
highlightTextRect.anchorMax = Vector2.one;
|
||||||
highlightTextRect.offsetMin = new Vector2(20, 0);
|
highlightTextRect.offsetMin = Vector2.zero;
|
||||||
highlightTextRect.offsetMax = new Vector2(14, 0);
|
highlightTextRect.offsetMax = Vector2.zero;
|
||||||
|
|
||||||
HighlightText = highlightTextObj.AddComponent<Text>();
|
HighlightText = highlightTextObj.AddComponent<Text>();
|
||||||
HighlightText.color = Color.clear;
|
HighlightText.color = Color.clear;
|
||||||
HighlightText.supportRichText = true;
|
HighlightText.supportRichText = true;
|
||||||
HighlightText.fontSize = fontSize;
|
HighlightText.fontSize = fontSize;
|
||||||
|
|
||||||
|
// Set fonts
|
||||||
|
InputText.font = UIManager.ConsoleFont;
|
||||||
|
Input.PlaceholderText.font = UIManager.ConsoleFont;
|
||||||
|
HighlightText.font = UIManager.ConsoleFont;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region COMPILE BUTTON BAR
|
#region COMPILE BUTTON BAR
|
||||||
@ -196,15 +159,6 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
InputText.font = UIManager.ConsoleFont;
|
|
||||||
placeHolderText.font = UIManager.ConsoleFont;
|
|
||||||
HighlightText.font = UIManager.ConsoleFont;
|
|
||||||
|
|
||||||
// reset this after formatting finalized
|
|
||||||
highlightTextRect.anchorMin = Vector2.zero;
|
|
||||||
highlightTextRect.anchorMax = Vector2.one;
|
|
||||||
highlightTextRect.offsetMin = Vector2.zero;
|
|
||||||
highlightTextRect.offsetMax = Vector2.zero;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user