mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-26 18:12:45 +08:00
Implement auto-indenting for CSConsole, some cleanups
This commit is contained in:
@ -37,7 +37,23 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
/// <summary>Key: Type.FullName</summary>
|
/// <summary>Key: Type.FullName</summary>
|
||||||
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
||||||
private static readonly SortedSet<string> allTypeNames = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
|
//private static readonly SortedSet<string> allTypeNames = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private static string[] allTypesArray;
|
||||||
|
private static string[] GetTypeNameArray()
|
||||||
|
{
|
||||||
|
if (allTypesArray == null || allTypesArray.Length != AllTypes.Count)
|
||||||
|
{
|
||||||
|
allTypesArray = new string[AllTypes.Count];
|
||||||
|
int i = 0;
|
||||||
|
foreach (var name in AllTypes.Keys)
|
||||||
|
{
|
||||||
|
allTypesArray[i] = name;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allTypesArray;
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetupTypeCache()
|
private static void SetupTypeCache()
|
||||||
{
|
{
|
||||||
@ -64,7 +80,7 @@ namespace UnityExplorer
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AllTypes.Add(type.FullName, type);
|
AllTypes.Add(type.FullName, type);
|
||||||
allTypeNames.Add(type.FullName);
|
//allTypeNames.Add(type.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnTypeLoaded?.Invoke(type);
|
OnTypeLoaded?.Invoke(type);
|
||||||
@ -211,14 +227,24 @@ namespace UnityExplorer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseType">The base type, which can optionally be abstract / interface.</param>
|
/// <param name="baseType">The base type, which can optionally be abstract / interface.</param>
|
||||||
/// <returns>All implementations of the type in the current AppDomain.</returns>
|
/// <returns>All implementations of the type in the current AppDomain.</returns>
|
||||||
public static HashSet<Type> GetImplementationsOf(Type baseType, bool allowAbstract, bool allowGeneric)
|
public static HashSet<Type> GetImplementationsOf(Type baseType, bool allowAbstract, bool allowGeneric, bool allowRecursive = true)
|
||||||
{
|
{
|
||||||
var key = GetImplementationKey(baseType); //baseType.FullName;
|
var key = GetImplementationKey(baseType);
|
||||||
|
|
||||||
|
int count = AllTypes.Count;
|
||||||
|
HashSet<Type> ret;
|
||||||
if (!baseType.IsGenericParameter)
|
if (!baseType.IsGenericParameter)
|
||||||
return GetImplementations(key, baseType, allowAbstract, allowGeneric);
|
ret = GetImplementations(key, baseType, allowAbstract, allowGeneric);
|
||||||
else
|
else
|
||||||
return GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric);
|
ret = GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric);
|
||||||
|
|
||||||
|
// types were resolved during the parse, do it again if we're not already rebuilding.
|
||||||
|
if (allowRecursive && AllTypes.Count != count)
|
||||||
|
{
|
||||||
|
ret = GetImplementationsOf(baseType, allowAbstract, allowGeneric, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HashSet<Type> GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric)
|
private static HashSet<Type> GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric)
|
||||||
@ -226,8 +252,10 @@ namespace UnityExplorer
|
|||||||
if (!typeInheritance.ContainsKey(key))
|
if (!typeInheritance.ContainsKey(key))
|
||||||
{
|
{
|
||||||
var set = new HashSet<Type>();
|
var set = new HashSet<Type>();
|
||||||
foreach (var name in allTypeNames)
|
var names = GetTypeNameArray();
|
||||||
|
for (int i = 0; i < names.Length; i++)
|
||||||
{
|
{
|
||||||
|
var name = names[i];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var type = AllTypes[name];
|
var type = AllTypes[name];
|
||||||
@ -263,8 +291,10 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
var set = new HashSet<Type>();
|
var set = new HashSet<Type>();
|
||||||
|
|
||||||
foreach (var name in allTypeNames)
|
var names = GetTypeNameArray();
|
||||||
|
for (int i = 0; i < names.Length; i++)
|
||||||
{
|
{
|
||||||
|
var name = names[i];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var type = AllTypes[name];
|
var type = AllTypes[name];
|
||||||
|
@ -10,9 +10,6 @@ using UnityEngine.Events;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.CSharp;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Runtime.Mono
|
namespace UnityExplorer.Core.Runtime.Mono
|
||||||
{
|
{
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -44,6 +44,7 @@ namespace UnityExplorer
|
|||||||
public static readonly Color StringOrange = new Color(0.83f, 0.61f, 0.52f);
|
public static readonly Color StringOrange = new Color(0.83f, 0.61f, 0.52f);
|
||||||
public static readonly Color EnumGreen = new Color(0.57f, 0.76f, 0.43f);
|
public static readonly Color EnumGreen = new Color(0.57f, 0.76f, 0.43f);
|
||||||
public static readonly Color KeywordBlue = new Color(0.3f, 0.61f, 0.83f);
|
public static readonly Color KeywordBlue = new Color(0.3f, 0.61f, 0.83f);
|
||||||
|
public static readonly string keywordBlueHex = KeywordBlue.ToHex();
|
||||||
public static readonly Color NumberGreen = new Color(0.71f, 0.8f, 0.65f);
|
public static readonly Color NumberGreen = new Color(0.71f, 0.8f, 0.65f);
|
||||||
|
|
||||||
internal static string GetClassColor(Type type)
|
internal static string GetClassColor(Type type)
|
||||||
|
@ -3,70 +3,95 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.UI.CSharpConsole.Lexers;
|
using UnityExplorer.UI.CSConsole.Lexers;
|
||||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole
|
namespace UnityExplorer.UI.CSConsole
|
||||||
{
|
{
|
||||||
public class CSAutoCompleter : ISuggestionProvider
|
public class CSAutoCompleter : ISuggestionProvider
|
||||||
{
|
{
|
||||||
public InputFieldRef InputField => CSConsole.Input;
|
public InputFieldRef InputField => ConsoleController.Input;
|
||||||
|
|
||||||
public bool AnchorToCaretPosition => true;
|
public bool AnchorToCaretPosition => true;
|
||||||
|
|
||||||
public void OnSuggestionClicked(Suggestion suggestion)
|
public void OnSuggestionClicked(Suggestion suggestion)
|
||||||
{
|
{
|
||||||
CSConsole.InsertSuggestionAtCaret(suggestion.UnderlyingValue);
|
ConsoleController.InsertSuggestionAtCaret(suggestion.UnderlyingValue);
|
||||||
|
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delimiters for completions, notably does not include '.'
|
||||||
private readonly HashSet<char> delimiters = new HashSet<char>
|
private readonly HashSet<char> delimiters = new HashSet<char>
|
||||||
{
|
{
|
||||||
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
|
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
|
||||||
};
|
};
|
||||||
|
|
||||||
//private readonly HashSet<string> expressions = new HashSet<string>
|
private readonly List<Suggestion> suggestions = new List<Suggestion>();
|
||||||
//{
|
|
||||||
// "new",
|
|
||||||
// "is", "as",
|
|
||||||
// "return", "yield", "throw", "in",
|
|
||||||
// "do", "for", "foreach",
|
|
||||||
// "else",
|
|
||||||
//};
|
|
||||||
|
|
||||||
public void CheckAutocompletes()
|
public void CheckAutocompletes()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(InputField.Text))
|
if (string.IsNullOrEmpty(InputField.Text))
|
||||||
{
|
{
|
||||||
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
|
suggestions.Clear();
|
||||||
int i = caret;
|
|
||||||
|
|
||||||
while (i > 0)
|
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
|
||||||
|
int start = caret;
|
||||||
|
|
||||||
|
// If the character at the caret index is whitespace or delimiter,
|
||||||
|
// or if the next character (if it exists) is not whitespace,
|
||||||
|
// then we don't want to provide suggestions.
|
||||||
|
if (char.IsWhiteSpace(InputField.Text[caret])
|
||||||
|
|| delimiters.Contains(InputField.Text[caret])
|
||||||
|
|| (InputField.Text.Length > caret + 1 && !char.IsWhiteSpace(InputField.Text[caret + 1])))
|
||||||
{
|
{
|
||||||
i--;
|
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||||
char c = InputField.Text[i];
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the current composition string (from caret back to last delimiter or whitespace)
|
||||||
|
while (start > 0)
|
||||||
|
{
|
||||||
|
start--;
|
||||||
|
char c = InputField.Text[start];
|
||||||
if (char.IsWhiteSpace(c) || delimiters.Contains(c))
|
if (char.IsWhiteSpace(c) || delimiters.Contains(c))
|
||||||
{
|
{
|
||||||
i++;
|
start++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
string input = InputField.Text.Substring(start, caret - start + 1);
|
||||||
|
|
||||||
i = Math.Max(0, i);
|
// Get MCS completions
|
||||||
|
|
||||||
string input = InputField.Text.Substring(i, (caret - i + 1));
|
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
|
||||||
|
|
||||||
string[] evaluatorCompletions = CSConsole.Evaluator.GetCompletions(input, out string prefix);
|
if (!string.IsNullOrEmpty(prefix) && evaluatorCompletions != null && evaluatorCompletions.Any())
|
||||||
|
|
||||||
if (evaluatorCompletions != null && evaluatorCompletions.Any())
|
|
||||||
{
|
{
|
||||||
var suggestions = from completion in evaluatorCompletions
|
suggestions.AddRange(from completion in evaluatorCompletions
|
||||||
select new Suggestion($"<color=cyan>{prefix}</color>{completion}", completion);
|
select new Suggestion($"<color=cyan>{prefix}</color>{completion}", completion));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get manual keyword completions
|
||||||
|
|
||||||
|
foreach (var kw in KeywordLexer.keywords)
|
||||||
|
{
|
||||||
|
if (kw.StartsWith(input))
|
||||||
|
{
|
||||||
|
string completion = kw.Substring(input.Length, kw.Length - input.Length);
|
||||||
|
|
||||||
|
suggestions.Add(new Suggestion(
|
||||||
|
$"<color=cyan>{input}</color>" +
|
||||||
|
$"<color=#{SignatureHighlighter.keywordBlueHex}>{completion}</color>",
|
||||||
|
completion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestions.Any())
|
||||||
|
{
|
||||||
AutoCompleteModal.Instance.TakeOwnership(this);
|
AutoCompleteModal.Instance.TakeOwnership(this);
|
||||||
AutoCompleteModal.Instance.SetSuggestions(suggestions);
|
AutoCompleteModal.Instance.SetSuggestions(suggestions);
|
||||||
}
|
}
|
||||||
|
@ -1,506 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.CSharp;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
using UnityExplorer.UI.Panels;
|
|
||||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole
|
|
||||||
{
|
|
||||||
public static class CSConsole
|
|
||||||
{
|
|
||||||
#region Strings / defaults
|
|
||||||
|
|
||||||
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
|
||||||
|
|
||||||
The following helper methods are available:
|
|
||||||
|
|
||||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
|
||||||
|
|
||||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
|
||||||
|
|
||||||
* <color=#add490>CurrentTarget()</color> returns the target of the active Inspector tab as System.Object
|
|
||||||
|
|
||||||
* <color=#add490>AllTargets()</color> returns a System.Object[] array containing the targets of all active tabs
|
|
||||||
|
|
||||||
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
|
||||||
|
|
||||||
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
|
||||||
|
|
||||||
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
|
||||||
|
|
||||||
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
|
||||||
|
|
||||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
|
||||||
";
|
|
||||||
|
|
||||||
internal static readonly string[] DefaultUsing = new string[]
|
|
||||||
{
|
|
||||||
"System",
|
|
||||||
"System.Linq",
|
|
||||||
"System.Collections",
|
|
||||||
"System.Collections.Generic",
|
|
||||||
"System.Reflection",
|
|
||||||
"UnityEngine",
|
|
||||||
#if CPP
|
|
||||||
"UnhollowerBaseLib",
|
|
||||||
"UnhollowerRuntimeLib",
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public static ScriptEvaluator Evaluator;
|
|
||||||
public static LexerBuilder Lexer;
|
|
||||||
public static CSAutoCompleter Completer;
|
|
||||||
|
|
||||||
private static HashSet<string> usingDirectives;
|
|
||||||
private static StringBuilder evaluatorOutput;
|
|
||||||
|
|
||||||
public static CSConsolePanel Panel => UIManager.CSharpConsole;
|
|
||||||
public static InputFieldRef Input => Panel.Input;
|
|
||||||
|
|
||||||
public static int LastCaretPosition { get; private set; }
|
|
||||||
internal static float defaultInputFieldAlpha;
|
|
||||||
|
|
||||||
// Todo save as config?
|
|
||||||
public static bool EnableCtrlRShortcut { get; private set; } = true;
|
|
||||||
public static bool EnableAutoIndent { get; private set; } = true;
|
|
||||||
public static bool EnableSuggestions { get; private set; } = true;
|
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ResetConsole(false);
|
|
||||||
Evaluator.Compile("0 == 0");
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("C# Console probably not supported, todo");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lexer = new LexerBuilder();
|
|
||||||
Completer = new CSAutoCompleter();
|
|
||||||
|
|
||||||
Panel.OnInputChanged += OnConsoleInputChanged;
|
|
||||||
Panel.InputScroll.OnScroll += OnInputScrolled;
|
|
||||||
Panel.OnCompileClicked += Evaluate;
|
|
||||||
Panel.OnResetClicked += ResetConsole;
|
|
||||||
Panel.OnAutoIndentToggled += OnToggleAutoIndent;
|
|
||||||
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
|
|
||||||
Panel.OnSuggestionsToggled += OnToggleSuggestions;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating and event listeners
|
|
||||||
|
|
||||||
private static void OnInputScrolled() => HighlightVisibleInput(Input.Text);
|
|
||||||
|
|
||||||
// Invoked at most once per frame
|
|
||||||
private static void OnConsoleInputChanged(string value)
|
|
||||||
{
|
|
||||||
LastCaretPosition = Input.Component.caretPosition;
|
|
||||||
|
|
||||||
if (EnableSuggestions)
|
|
||||||
Completer.CheckAutocompletes();
|
|
||||||
|
|
||||||
HighlightVisibleInput(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Update()
|
|
||||||
{
|
|
||||||
int lastCaretPos = LastCaretPosition;
|
|
||||||
UpdateCaret();
|
|
||||||
bool caretMoved = lastCaretPos != LastCaretPosition;
|
|
||||||
|
|
||||||
if (EnableSuggestions && caretMoved)
|
|
||||||
{
|
|
||||||
Completer.CheckAutocompletes();
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (EnableAutoIndent && caretMoved)
|
|
||||||
// DoAutoIndent();
|
|
||||||
|
|
||||||
if (EnableCtrlRShortcut
|
|
||||||
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
|
||||||
&& InputManager.GetKeyDown(KeyCode.R))
|
|
||||||
{
|
|
||||||
Evaluate(Panel.Input.Text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateCaret()
|
|
||||||
{
|
|
||||||
LastCaretPosition = Input.Component.caretPosition;
|
|
||||||
|
|
||||||
// todo check if out of bounds, move content if so
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region Evaluating
|
|
||||||
|
|
||||||
public static void ResetConsole() => ResetConsole(true);
|
|
||||||
|
|
||||||
public static void ResetConsole(bool logSuccess = true)
|
|
||||||
{
|
|
||||||
if (Evaluator != null)
|
|
||||||
Evaluator.Dispose();
|
|
||||||
|
|
||||||
evaluatorOutput = new StringBuilder();
|
|
||||||
Evaluator = new ScriptEvaluator(new StringWriter(evaluatorOutput))
|
|
||||||
{
|
|
||||||
InteractiveBaseClass = typeof(ScriptInteraction)
|
|
||||||
};
|
|
||||||
|
|
||||||
usingDirectives = new HashSet<string>();
|
|
||||||
foreach (var use in DefaultUsing)
|
|
||||||
AddUsing(use);
|
|
||||||
|
|
||||||
if (logSuccess)
|
|
||||||
ExplorerCore.Log($"C# Console reset. Using directives:\r\n{Evaluator.GetUsing()}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddUsing(string assemblyName)
|
|
||||||
{
|
|
||||||
if (!usingDirectives.Contains(assemblyName))
|
|
||||||
{
|
|
||||||
Evaluate($"using {assemblyName};", true);
|
|
||||||
usingDirectives.Add(assemblyName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Evaluate()
|
|
||||||
{
|
|
||||||
Evaluate(Input.Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Evaluate(string input, bool supressLog = false)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Evaluator.Run(input);
|
|
||||||
|
|
||||||
string output = ScriptEvaluator._textWriter.ToString();
|
|
||||||
var outputSplit = output.Split('\n');
|
|
||||||
if (outputSplit.Length >= 2)
|
|
||||||
output = outputSplit[outputSplit.Length - 2];
|
|
||||||
evaluatorOutput.Clear();
|
|
||||||
|
|
||||||
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
|
||||||
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
|
||||||
|
|
||||||
if (!supressLog)
|
|
||||||
ExplorerCore.Log("Code executed successfully.");
|
|
||||||
}
|
|
||||||
catch (FormatException fex)
|
|
||||||
{
|
|
||||||
if (!supressLog)
|
|
||||||
ExplorerCore.LogWarning(fex.Message);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (!supressLog)
|
|
||||||
ExplorerCore.LogWarning(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region Lexer Highlighting
|
|
||||||
|
|
||||||
private static void HighlightVisibleInput(string value)
|
|
||||||
{
|
|
||||||
int startIdx = 0;
|
|
||||||
int endIdx = value.Length - 1;
|
|
||||||
int topLine = 0;
|
|
||||||
|
|
||||||
// Calculate visible text if necessary
|
|
||||||
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
|
|
||||||
{
|
|
||||||
topLine = -1;
|
|
||||||
int bottomLine = -1;
|
|
||||||
|
|
||||||
// the top and bottom position of the viewport in relation to the text height
|
|
||||||
// they need the half-height adjustment to normalize against the 'line.topY' value.
|
|
||||||
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
|
|
||||||
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height - (Input.Rect.rect.height * 0.5f);
|
|
||||||
|
|
||||||
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
|
|
||||||
{
|
|
||||||
var line = Input.TextGenerator.lines[i];
|
|
||||||
// if not set the top line yet, and top of line is below the viewport top
|
|
||||||
if (topLine == -1 && line.topY <= viewportMin)
|
|
||||||
topLine = i;
|
|
||||||
// if bottom of line is below the viewport bottom
|
|
||||||
if ((line.topY - line.height) >= viewportMax)
|
|
||||||
bottomLine = i;
|
|
||||||
}
|
|
||||||
// make sure lines are valid
|
|
||||||
topLine = Math.Max(0, topLine - 1);
|
|
||||||
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
|
|
||||||
|
|
||||||
startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
|
|
||||||
endIdx = bottomLine == Input.TextGenerator.lineCount
|
|
||||||
? value.Length - 1
|
|
||||||
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Highlight the visible text with the LexerBuilder
|
|
||||||
Panel.HighlightText.text = Lexer.BuildHighlightedString(value, startIdx, endIdx, topLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region Autocompletes
|
|
||||||
|
|
||||||
public static void InsertSuggestionAtCaret(string suggestion)
|
|
||||||
{
|
|
||||||
string input = Input.Text;
|
|
||||||
input = input.Insert(LastCaretPosition, suggestion);
|
|
||||||
Input.Text = input;
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaret(LastCaretPosition += suggestion.Length));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerator SetAutocompleteCaret(int caretPosition)
|
|
||||||
{
|
|
||||||
var color = Input.Component.selectionColor;
|
|
||||||
color.a = 0f;
|
|
||||||
Input.Component.selectionColor = color;
|
|
||||||
yield return null;
|
|
||||||
|
|
||||||
EventSystem.current.SetSelectedGameObject(Panel.Input.UIRoot, null);
|
|
||||||
yield return null;
|
|
||||||
|
|
||||||
Input.Component.caretPosition = caretPosition;
|
|
||||||
Input.Component.selectionFocusPosition = caretPosition;
|
|
||||||
color.a = defaultInputFieldAlpha;
|
|
||||||
Input.Component.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
|
|
||||||
|
|
||||||
|
|
||||||
#region UI Listeners and options
|
|
||||||
|
|
||||||
private static void OnToggleAutoIndent(bool value)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnToggleCtrlRShortcut(bool value)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnToggleSuggestions(bool value)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
392
src/UI/CSConsole/ConsoleController.cs
Normal file
392
src/UI/CSConsole/ConsoleController.cs
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.UI.CSConsole;
|
||||||
|
using UnityExplorer.Core.Input;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.CSConsole
|
||||||
|
{
|
||||||
|
public static class ConsoleController
|
||||||
|
{
|
||||||
|
#region Strings / defaults
|
||||||
|
|
||||||
|
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
||||||
|
|
||||||
|
The following helper methods are available:
|
||||||
|
|
||||||
|
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
||||||
|
|
||||||
|
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
||||||
|
|
||||||
|
* <color=#add490>CurrentTarget()</color> returns the target of the active Inspector tab as System.Object
|
||||||
|
|
||||||
|
* <color=#add490>AllTargets()</color> returns a System.Object[] array containing the targets of all active tabs
|
||||||
|
|
||||||
|
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
||||||
|
|
||||||
|
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
||||||
|
|
||||||
|
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
||||||
|
|
||||||
|
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
||||||
|
|
||||||
|
* <color=#add490>Reset()</color> resets all using directives and variables
|
||||||
|
";
|
||||||
|
|
||||||
|
internal static readonly string[] DefaultUsing = new string[]
|
||||||
|
{
|
||||||
|
"System",
|
||||||
|
"System.Linq",
|
||||||
|
"System.Collections",
|
||||||
|
"System.Collections.Generic",
|
||||||
|
"System.Reflection",
|
||||||
|
"UnityEngine",
|
||||||
|
#if CPP
|
||||||
|
"UnhollowerBaseLib",
|
||||||
|
"UnhollowerRuntimeLib",
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static ScriptEvaluator Evaluator;
|
||||||
|
public static LexerBuilder Lexer;
|
||||||
|
public static CSAutoCompleter Completer;
|
||||||
|
|
||||||
|
private static HashSet<string> usingDirectives;
|
||||||
|
private static StringBuilder evaluatorOutput;
|
||||||
|
|
||||||
|
public static CSConsolePanel Panel => UIManager.CSharpConsole;
|
||||||
|
public static InputFieldRef Input => Panel.Input;
|
||||||
|
|
||||||
|
public static int LastCaretPosition { get; private set; }
|
||||||
|
public static int PreviousCaretPosition { get; private set; }
|
||||||
|
internal static float defaultInputFieldAlpha;
|
||||||
|
|
||||||
|
// Todo save as config?
|
||||||
|
public static bool EnableCtrlRShortcut { get; private set; } = true;
|
||||||
|
public static bool EnableAutoIndent { get; private set; } = true;
|
||||||
|
public static bool EnableSuggestions { get; private set; } = true;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ResetConsole(false);
|
||||||
|
Evaluator.Compile("0 == 0");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("C# Console probably not supported, todo");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer = new LexerBuilder();
|
||||||
|
Completer = new CSAutoCompleter();
|
||||||
|
|
||||||
|
Panel.OnInputChanged += OnInputChanged;
|
||||||
|
Panel.InputScroll.OnScroll += OnInputScrolled;
|
||||||
|
Panel.OnCompileClicked += Evaluate;
|
||||||
|
Panel.OnResetClicked += ResetConsole;
|
||||||
|
Panel.OnAutoIndentToggled += OnToggleAutoIndent;
|
||||||
|
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
|
||||||
|
Panel.OnSuggestionsToggled += OnToggleSuggestions;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#region UI Listeners and options
|
||||||
|
|
||||||
|
// TODO save
|
||||||
|
|
||||||
|
private static void OnToggleAutoIndent(bool value)
|
||||||
|
{
|
||||||
|
EnableAutoIndent = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnToggleCtrlRShortcut(bool value)
|
||||||
|
{
|
||||||
|
EnableCtrlRShortcut = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnToggleSuggestions(bool value)
|
||||||
|
{
|
||||||
|
EnableSuggestions = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Updating and event listeners
|
||||||
|
|
||||||
|
private static bool settingAutoCompletion;
|
||||||
|
|
||||||
|
private static void OnInputScrolled() => HighlightVisibleInput();
|
||||||
|
|
||||||
|
// Invoked at most once per frame
|
||||||
|
private static void OnInputChanged(string value)
|
||||||
|
{
|
||||||
|
if (!settingAutoCompletion && EnableSuggestions)
|
||||||
|
Completer.CheckAutocompletes();
|
||||||
|
|
||||||
|
if (!settingAutoCompletion && EnableAutoIndent)
|
||||||
|
DoAutoIndent();
|
||||||
|
|
||||||
|
HighlightVisibleInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Update()
|
||||||
|
{
|
||||||
|
int lastCaretPos = LastCaretPosition;
|
||||||
|
UpdateCaret();
|
||||||
|
bool caretMoved = lastCaretPos != LastCaretPosition;
|
||||||
|
|
||||||
|
if (!settingAutoCompletion && EnableSuggestions && caretMoved)
|
||||||
|
{
|
||||||
|
Completer.CheckAutocompletes();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnableCtrlRShortcut
|
||||||
|
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||||
|
&& InputManager.GetKeyDown(KeyCode.R))
|
||||||
|
{
|
||||||
|
Evaluate(Panel.Input.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int CSCONSOLE_LINEHEIGHT = 18;
|
||||||
|
|
||||||
|
private static void UpdateCaret()
|
||||||
|
{
|
||||||
|
if (Input.Component.isFocused)
|
||||||
|
{
|
||||||
|
PreviousCaretPosition = LastCaretPosition;
|
||||||
|
LastCaretPosition = Input.Component.caretPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input.Text.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var charInfo = Input.TextGenerator.characters[LastCaretPosition];
|
||||||
|
var charTop = charInfo.cursorPos.y;
|
||||||
|
var charBot = charTop - CSCONSOLE_LINEHEIGHT;
|
||||||
|
|
||||||
|
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
|
||||||
|
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
|
||||||
|
|
||||||
|
float diff = 0f;
|
||||||
|
if (charTop > viewportMin)
|
||||||
|
diff = charTop - viewportMin;
|
||||||
|
else if (charBot < viewportMax)
|
||||||
|
diff = charBot - viewportMax;
|
||||||
|
|
||||||
|
if (Math.Abs(diff) > 1)
|
||||||
|
{
|
||||||
|
var rect = Input.Rect;
|
||||||
|
rect.anchoredPosition = new Vector2(rect.anchoredPosition.x, rect.anchoredPosition.y - diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Evaluating
|
||||||
|
|
||||||
|
public static void ResetConsole() => ResetConsole(true);
|
||||||
|
|
||||||
|
public static void ResetConsole(bool logSuccess = true)
|
||||||
|
{
|
||||||
|
if (Evaluator != null)
|
||||||
|
Evaluator.Dispose();
|
||||||
|
|
||||||
|
evaluatorOutput = new StringBuilder();
|
||||||
|
Evaluator = new ScriptEvaluator(new StringWriter(evaluatorOutput))
|
||||||
|
{
|
||||||
|
InteractiveBaseClass = typeof(ScriptInteraction)
|
||||||
|
};
|
||||||
|
|
||||||
|
usingDirectives = new HashSet<string>();
|
||||||
|
foreach (var use in DefaultUsing)
|
||||||
|
AddUsing(use);
|
||||||
|
|
||||||
|
if (logSuccess)
|
||||||
|
ExplorerCore.Log($"C# Console reset. Using directives:\r\n{Evaluator.GetUsing()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddUsing(string assemblyName)
|
||||||
|
{
|
||||||
|
if (!usingDirectives.Contains(assemblyName))
|
||||||
|
{
|
||||||
|
Evaluate($"using {assemblyName};", true);
|
||||||
|
usingDirectives.Add(assemblyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Evaluate()
|
||||||
|
{
|
||||||
|
Evaluate(Input.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Evaluate(string input, bool supressLog = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Evaluator.Run(input);
|
||||||
|
|
||||||
|
string output = ScriptEvaluator._textWriter.ToString();
|
||||||
|
var outputSplit = output.Split('\n');
|
||||||
|
if (outputSplit.Length >= 2)
|
||||||
|
output = outputSplit[outputSplit.Length - 2];
|
||||||
|
evaluatorOutput.Clear();
|
||||||
|
|
||||||
|
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
||||||
|
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
||||||
|
|
||||||
|
if (!supressLog)
|
||||||
|
ExplorerCore.Log("Code executed successfully.");
|
||||||
|
}
|
||||||
|
catch (FormatException fex)
|
||||||
|
{
|
||||||
|
if (!supressLog)
|
||||||
|
ExplorerCore.LogWarning(fex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (!supressLog)
|
||||||
|
ExplorerCore.LogWarning(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Lexer Highlighting
|
||||||
|
|
||||||
|
private static void HighlightVisibleInput()
|
||||||
|
{
|
||||||
|
int startIdx = 0;
|
||||||
|
int endIdx = Input.Text.Length - 1;
|
||||||
|
int topLine = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Calculate visible text if necessary
|
||||||
|
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
|
||||||
|
{
|
||||||
|
topLine = -1;
|
||||||
|
int bottomLine = -1;
|
||||||
|
|
||||||
|
// the top and bottom position of the viewport in relation to the text height
|
||||||
|
// they need the half-height adjustment to normalize against the 'line.topY' value.
|
||||||
|
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
|
||||||
|
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
|
||||||
|
|
||||||
|
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
|
||||||
|
{
|
||||||
|
var line = Input.TextGenerator.lines[i];
|
||||||
|
// if not set the top line yet, and top of line is below the viewport top
|
||||||
|
if (topLine == -1 && line.topY <= viewportMin)
|
||||||
|
topLine = i;
|
||||||
|
// if bottom of line is below the viewport bottom
|
||||||
|
if ((line.topY - line.height) >= viewportMax)
|
||||||
|
bottomLine = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
topLine = Math.Max(0, topLine - 1);
|
||||||
|
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
|
||||||
|
|
||||||
|
startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
|
||||||
|
endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
|
||||||
|
? Input.Text.Length - 1
|
||||||
|
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExplorerCore.Log("Exception on HighlightVisibleText: " + ex.GetType().Name + ", " + ex.Message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Highlight the visible text with the LexerBuilder
|
||||||
|
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Autocompletes
|
||||||
|
|
||||||
|
public static void InsertSuggestionAtCaret(string suggestion)
|
||||||
|
{
|
||||||
|
settingAutoCompletion = true;
|
||||||
|
Input.Text = Input.Text.Insert(LastCaretPosition, suggestion);
|
||||||
|
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaret(LastCaretPosition + suggestion.Length));
|
||||||
|
LastCaretPosition = Input.Component.caretPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator SetAutocompleteCaret(int caretPosition)
|
||||||
|
{
|
||||||
|
var color = Input.Component.selectionColor;
|
||||||
|
color.a = 0f;
|
||||||
|
Input.Component.selectionColor = color;
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
EventSystem.current.SetSelectedGameObject(Panel.Input.UIRoot, null);
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
Input.Component.caretPosition = caretPosition;
|
||||||
|
Input.Component.selectionFocusPosition = caretPosition;
|
||||||
|
LastCaretPosition = Input.Component.caretPosition;
|
||||||
|
|
||||||
|
color.a = defaultInputFieldAlpha;
|
||||||
|
Input.Component.selectionColor = color;
|
||||||
|
|
||||||
|
settingAutoCompletion = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Auto indenting
|
||||||
|
|
||||||
|
private static int prevContentLen = 0;
|
||||||
|
|
||||||
|
private static void DoAutoIndent()
|
||||||
|
{
|
||||||
|
if (Input.Text.Length > prevContentLen)
|
||||||
|
{
|
||||||
|
int inc = Input.Text.Length - prevContentLen;
|
||||||
|
|
||||||
|
if (inc == 1)
|
||||||
|
{
|
||||||
|
int caret = Input.Component.caretPosition;
|
||||||
|
Input.Text = Lexer.IndentCharacter(Input.Text, ref caret);
|
||||||
|
Input.Component.caretPosition = caret;
|
||||||
|
LastCaretPosition = caret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// todo indenting for copy+pasted content
|
||||||
|
|
||||||
|
//ExplorerCore.Log("Content increased by " + inc);
|
||||||
|
//var comp = Input.Text.Substring(PreviousCaretPosition, inc);
|
||||||
|
//ExplorerCore.Log("composition string: " + comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevContentLen = Input.Text.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
using System;
|
using Mono.CSharp;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.UI.CSharpConsole.Lexers;
|
using UnityExplorer.UI.CSConsole.Lexers;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole
|
namespace UnityExplorer.UI.CSConsole
|
||||||
{
|
{
|
||||||
public struct MatchInfo
|
public struct MatchInfo
|
||||||
{
|
{
|
||||||
@ -20,19 +21,22 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
#region Core and initialization
|
#region Core and initialization
|
||||||
|
|
||||||
public const char WHITESPACE = ' ';
|
public const char WHITESPACE = ' ';
|
||||||
public const char INDENT_OPEN = '{';
|
public readonly HashSet<char> IndentOpenChars = new HashSet<char> { '{', '(' };
|
||||||
public const char INDENT_CLOSE = '}';
|
public readonly HashSet<char> IndentCloseChars = new HashSet<char> { '}', ')' };
|
||||||
|
|
||||||
private readonly Lexer[] lexers;
|
private readonly Lexer[] lexers;
|
||||||
private readonly HashSet<char> delimiters = new HashSet<char>();
|
private readonly HashSet<char> delimiters = new HashSet<char>();
|
||||||
|
|
||||||
|
private readonly StringLexer stringLexer = new StringLexer();
|
||||||
|
private readonly CommentLexer commentLexer = new CommentLexer();
|
||||||
|
|
||||||
public LexerBuilder()
|
public LexerBuilder()
|
||||||
{
|
{
|
||||||
lexers = new Lexer[]
|
lexers = new Lexer[]
|
||||||
{
|
{
|
||||||
new CommentLexer(),
|
commentLexer,
|
||||||
|
stringLexer,
|
||||||
new SymbolLexer(),
|
new SymbolLexer(),
|
||||||
new StringLexer(),
|
|
||||||
new NumberLexer(),
|
new NumberLexer(),
|
||||||
new KeywordLexer(),
|
new KeywordLexer(),
|
||||||
};
|
};
|
||||||
@ -49,14 +53,22 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public int LastCommittedIndex { get; private set; }
|
/// <summary>The last committed index for a match or no-match. Starts at -1 for a new parse.</summary>
|
||||||
public int LookaheadIndex { get; private set; }
|
public int CommittedIndex { get; private set; }
|
||||||
|
/// <summary>The index of the character we are currently parsing, at minimum it will be CommittedIndex + 1.</summary>
|
||||||
|
public int CurrentIndex { get; private set; }
|
||||||
|
|
||||||
public char Current => !EndOfInput ? currentInput[LookaheadIndex] : WHITESPACE;
|
/// <summary>The current character we are parsing, determined by CurrentIndex.</summary>
|
||||||
public char Previous => LookaheadIndex >= 1 ? currentInput[LookaheadIndex - 1] : WHITESPACE;
|
public char Current => !EndOfInput ? currentInput[CurrentIndex] : WHITESPACE;
|
||||||
|
/// <summary>The previous character (CurrentIndex - 1), or whitespace if no previous character.</summary>
|
||||||
|
public char Previous => CurrentIndex >= 1 ? currentInput[CurrentIndex - 1] : WHITESPACE;
|
||||||
|
|
||||||
public bool EndOfInput => LookaheadIndex > currentEndIdx;
|
/// <summary>Returns true if CurrentIndex is >= the current input length.</summary>
|
||||||
public bool EndOrNewLine => EndOfInput || Current == '\n' || Current == '\r';
|
public bool EndOfInput => CurrentIndex > currentEndIdx;
|
||||||
|
/// <summary>Returns true if EndOfInput or current character is a new line.</summary>
|
||||||
|
public bool EndOrNewLine => EndOfInput || IsNewLine(Current);
|
||||||
|
|
||||||
|
public static bool IsNewLine(char c) => c == '\n' || c == '\r';
|
||||||
|
|
||||||
private string currentInput;
|
private string currentInput;
|
||||||
private int currentStartIdx;
|
private int currentStartIdx;
|
||||||
@ -72,6 +84,9 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
/// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns>
|
/// <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)
|
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
|
if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
|
||||||
return input;
|
return input;
|
||||||
|
|
||||||
@ -109,16 +124,16 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
// Match builder, iterates through each Lexer and returns all matches found.
|
// Match builder, iterates through each Lexer and returns all matches found.
|
||||||
|
|
||||||
private IEnumerable<MatchInfo> GetMatches()
|
public IEnumerable<MatchInfo> GetMatches()
|
||||||
{
|
{
|
||||||
LastCommittedIndex = currentStartIdx - 1;
|
CommittedIndex = currentStartIdx - 1;
|
||||||
Rollback();
|
Rollback();
|
||||||
|
|
||||||
while (!EndOfInput)
|
while (!EndOfInput)
|
||||||
{
|
{
|
||||||
SkipWhitespace();
|
SkipWhitespace();
|
||||||
bool anyMatch = false;
|
bool anyMatch = false;
|
||||||
int startIndex = LastCommittedIndex + 1;
|
int startIndex = CommittedIndex + 1;
|
||||||
|
|
||||||
foreach (var lexer in lexers)
|
foreach (var lexer in lexers)
|
||||||
{
|
{
|
||||||
@ -129,7 +144,7 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
yield return new MatchInfo
|
yield return new MatchInfo
|
||||||
{
|
{
|
||||||
startIndex = startIndex,
|
startIndex = startIndex,
|
||||||
endIndex = LastCommittedIndex,
|
endIndex = CommittedIndex,
|
||||||
htmlColorTag = lexer.ColorTag,
|
htmlColorTag = lexer.ColorTag,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@ -140,7 +155,7 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
if (!anyMatch)
|
if (!anyMatch)
|
||||||
{
|
{
|
||||||
LookaheadIndex = LastCommittedIndex + 1;
|
CurrentIndex = CommittedIndex + 1;
|
||||||
Commit();
|
Commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,23 +165,23 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
public char PeekNext(int amount = 1)
|
public char PeekNext(int amount = 1)
|
||||||
{
|
{
|
||||||
LookaheadIndex += amount;
|
CurrentIndex += amount;
|
||||||
return Current;
|
return Current;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Commit()
|
public void Commit()
|
||||||
{
|
{
|
||||||
LastCommittedIndex = Math.Min(currentEndIdx, LookaheadIndex);
|
CommittedIndex = Math.Min(currentEndIdx, CurrentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Rollback()
|
public void Rollback()
|
||||||
{
|
{
|
||||||
LookaheadIndex = LastCommittedIndex + 1;
|
CurrentIndex = CommittedIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RollbackBy(int amount)
|
public void RollbackBy(int amount)
|
||||||
{
|
{
|
||||||
LookaheadIndex = Math.Max(LastCommittedIndex + 1, LookaheadIndex - amount);
|
CurrentIndex = Math.Max(CommittedIndex + 1, CurrentIndex - amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDelimiter(char character, bool orWhitespace = false, bool orLetterOrDigit = false)
|
public bool IsDelimiter(char character, bool orWhitespace = false, bool orLetterOrDigit = false)
|
||||||
@ -185,8 +200,139 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
PeekNext();
|
PeekNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// revert the last PeekNext which would have returned false
|
if (!char.IsWhiteSpace(Current))
|
||||||
Rollback();
|
Rollback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Auto Indenting
|
||||||
|
|
||||||
|
// Using the Lexer for indenting as it already has what we need to tokenize strings and comments.
|
||||||
|
// At the moment this only handles when a single newline or close-delimiter is composed.
|
||||||
|
// Does not handle copy+paste or any other characters yet.
|
||||||
|
|
||||||
|
public string IndentCharacter(string input, ref int caretIndex)
|
||||||
|
{
|
||||||
|
int lastCharIndex = caretIndex - 1;
|
||||||
|
char c = input[lastCharIndex];
|
||||||
|
|
||||||
|
// we only want to indent for new lines and close indents
|
||||||
|
if (!IsNewLine(c) && !IndentCloseChars.Contains(c))
|
||||||
|
return input;
|
||||||
|
|
||||||
|
// perform a light parse up to the caret to determine indent level
|
||||||
|
currentInput = input;
|
||||||
|
currentStartIdx = 0;
|
||||||
|
currentEndIdx = lastCharIndex;
|
||||||
|
CommittedIndex = -1;
|
||||||
|
Rollback();
|
||||||
|
|
||||||
|
int indent = 0;
|
||||||
|
|
||||||
|
while (!EndOfInput)
|
||||||
|
{
|
||||||
|
if (CurrentIndex >= lastCharIndex)
|
||||||
|
{
|
||||||
|
// reached the caret index
|
||||||
|
if (indent <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (IsNewLine(c))
|
||||||
|
input = IndentNewLine(input, indent, ref caretIndex);
|
||||||
|
else // closing indent
|
||||||
|
input = IndentCloseDelimiter(input, indent, lastCharIndex, ref caretIndex);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try match strings and comments (Lexer will commit to the end of the match)
|
||||||
|
if (stringLexer.TryMatchCurrent(this) || commentLexer.TryMatchCurrent(this))
|
||||||
|
{
|
||||||
|
PeekNext();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still parsing, check indent
|
||||||
|
|
||||||
|
if (IndentOpenChars.Contains(Current))
|
||||||
|
indent++;
|
||||||
|
else if (IndentCloseChars.Contains(Current))
|
||||||
|
indent--;
|
||||||
|
|
||||||
|
Commit();
|
||||||
|
PeekNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string IndentNewLine(string input, int indent, ref int caretIndex)
|
||||||
|
{
|
||||||
|
// continue until the end of line or next non-whitespace character.
|
||||||
|
// if there's a close-indent on this line, reduce the indent level.
|
||||||
|
while (CurrentIndex < input.Length - 1)
|
||||||
|
{
|
||||||
|
CurrentIndex++;
|
||||||
|
char next = input[CurrentIndex];
|
||||||
|
if (IsNewLine(next))
|
||||||
|
break;
|
||||||
|
if (char.IsWhiteSpace(next))
|
||||||
|
continue;
|
||||||
|
else if (IndentCloseChars.Contains(next))
|
||||||
|
indent--;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indent > 0)
|
||||||
|
{
|
||||||
|
input = input.Insert(caretIndex, new string('\t', indent));
|
||||||
|
caretIndex += indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string IndentCloseDelimiter(string input, int indent, int lastCharIndex, ref int caretIndex)
|
||||||
|
{
|
||||||
|
if (CurrentIndex > lastCharIndex)
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lower the indent level by one as we would not have accounted for this closing symbol
|
||||||
|
indent--;
|
||||||
|
|
||||||
|
while (CurrentIndex > 0)
|
||||||
|
{
|
||||||
|
CurrentIndex--;
|
||||||
|
char prev = input[CurrentIndex];
|
||||||
|
if (IsNewLine(prev))
|
||||||
|
break;
|
||||||
|
if (!char.IsWhiteSpace(prev))
|
||||||
|
{
|
||||||
|
// the line containing the closing bracket has non-whitespace characters before it. do not indent.
|
||||||
|
indent = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (prev == '\t')
|
||||||
|
indent--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indent > 0)
|
||||||
|
{
|
||||||
|
input = input.Insert(caretIndex, new string('\t', indent));
|
||||||
|
caretIndex += indent;
|
||||||
|
}
|
||||||
|
else if (indent < 0)
|
||||||
|
{
|
||||||
|
// line is overly indented
|
||||||
|
input = input.Remove(lastCharIndex - 1, -indent);
|
||||||
|
caretIndex += indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole.Lexers
|
namespace UnityExplorer.UI.CSConsole.Lexers
|
||||||
{
|
{
|
||||||
public class CommentLexer : Lexer
|
public class CommentLexer : Lexer
|
||||||
{
|
{
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole.Lexers
|
namespace UnityExplorer.UI.CSConsole.Lexers
|
||||||
{
|
{
|
||||||
public class KeywordLexer : Lexer
|
public class KeywordLexer : Lexer
|
||||||
{
|
{
|
||||||
// system blue
|
// system blue
|
||||||
protected override Color HighlightColor => new Color(0.33f, 0.61f, 0.83f, 1.0f);
|
protected override Color HighlightColor => new Color(0.33f, 0.61f, 0.83f, 1.0f);
|
||||||
|
|
||||||
private readonly HashSet<string> keywords = new HashSet<string>
|
public static readonly HashSet<string> keywords = new HashSet<string>
|
||||||
{
|
{
|
||||||
// reserved keywords
|
// reserved keywords
|
||||||
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
|
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole.Lexers
|
namespace UnityExplorer.UI.CSConsole.Lexers
|
||||||
{
|
{
|
||||||
public abstract class Lexer
|
public abstract class Lexer
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole.Lexers
|
namespace UnityExplorer.UI.CSConsole.Lexers
|
||||||
{
|
{
|
||||||
public class NumberLexer : Lexer
|
public class NumberLexer : Lexer
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole.Lexers
|
namespace UnityExplorer.UI.CSConsole.Lexers
|
||||||
{
|
{
|
||||||
public class StringLexer : Lexer
|
public class StringLexer : Lexer
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole.Lexers
|
namespace UnityExplorer.UI.CSConsole.Lexers
|
||||||
{
|
{
|
||||||
public class SymbolLexer : Lexer
|
public class SymbolLexer : Lexer
|
||||||
{
|
{
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
using Mono.CSharp;
|
using Mono.CSharp;
|
||||||
|
|
||||||
// Thanks to ManlyMarco for this
|
// Thanks to ManlyMarco for this
|
||||||
|
|
||||||
namespace UnityExplorer.Core.CSharp
|
namespace UnityExplorer.UI.CSConsole
|
||||||
{
|
{
|
||||||
public class ScriptEvaluator : Evaluator, IDisposable
|
public class ScriptEvaluator : Evaluator, IDisposable
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSharpConsole
|
namespace UnityExplorer.UI.CSConsole
|
||||||
{
|
{
|
||||||
public class ScriptInteraction : InteractiveBase
|
public class ScriptInteraction : InteractiveBase
|
||||||
{
|
{
|
||||||
@ -22,17 +22,17 @@ namespace UnityExplorer.UI.CSharpConsole
|
|||||||
|
|
||||||
public static void AddUsing(string directive)
|
public static void AddUsing(string directive)
|
||||||
{
|
{
|
||||||
CSConsole.AddUsing(directive);
|
ConsoleController.AddUsing(directive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GetUsing()
|
public static void GetUsing()
|
||||||
{
|
{
|
||||||
ExplorerCore.Log(CSConsole.Evaluator.GetUsing());
|
ExplorerCore.Log(ConsoleController.Evaluator.GetUsing());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
CSConsole.ResetConsole();
|
ConsoleController.ResetConsole();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object CurrentTarget()
|
public static object CurrentTarget()
|
||||||
|
@ -338,9 +338,9 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
// Blacklists
|
// Blacklists
|
||||||
private static readonly HashSet<string> bl_typeAndMember = new HashSet<string>
|
private static readonly HashSet<string> bl_typeAndMember = new HashSet<string>
|
||||||
{
|
{
|
||||||
// these cause a crash in IL2CPP
|
// these can cause a crash in IL2CPP
|
||||||
#if CPP
|
#if CPP
|
||||||
//"Type.DeclaringMethod",
|
"Type.DeclaringMethod",
|
||||||
"Rigidbody2D.Cast",
|
"Rigidbody2D.Cast",
|
||||||
"Collider2D.Cast",
|
"Collider2D.Cast",
|
||||||
"Collider2D.Raycast",
|
"Collider2D.Raycast",
|
||||||
@ -363,6 +363,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
"Component.renderer",
|
"Component.renderer",
|
||||||
"Component.rigidbody",
|
"Component.rigidbody",
|
||||||
"Component.rigidbody2D",
|
"Component.rigidbody2D",
|
||||||
|
"Light.flare",
|
||||||
};
|
};
|
||||||
private static readonly HashSet<string> bl_methodNameStartsWith = new HashSet<string>
|
private static readonly HashSet<string> bl_methodNameStartsWith = new HashSet<string>
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@ using UnityEngine;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityExplorer.UI.CSharpConsole;
|
using UnityExplorer.UI.CSConsole;
|
||||||
using UnityExplorer.UI.Utility;
|
using UnityExplorer.UI.Utility;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Panels
|
namespace UnityExplorer.UI.Panels
|
||||||
@ -44,7 +44,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
CSConsole.Update();
|
ConsoleController.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saving
|
// Saving
|
||||||
@ -111,9 +111,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
int fontSize = 16;
|
int fontSize = 16;
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsole.STARTUP_TEXT, out var inputScroller, fontSize);
|
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", ConsoleController.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||||
InputScroll = inputScroller;
|
InputScroll = inputScroller;
|
||||||
CSConsole.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
||||||
Input.OnValueChanged += InvokeOnValueChanged;
|
Input.OnValueChanged += InvokeOnValueChanged;
|
||||||
|
|
||||||
InputText = Input.Component.textComponent;
|
InputText = Input.Component.textComponent;
|
||||||
|
@ -8,7 +8,7 @@ using UnityEngine.EventSystems;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityExplorer.Core.Input;
|
using UnityExplorer.Core.Input;
|
||||||
using UnityExplorer.UI.CSharpConsole;
|
using UnityExplorer.UI.CSConsole;
|
||||||
using UnityExplorer.UI.Models;
|
using UnityExplorer.UI.Models;
|
||||||
using UnityExplorer.UI.Panels;
|
using UnityExplorer.UI.Panels;
|
||||||
using UnityExplorer.UI.Utility;
|
using UnityExplorer.UI.Utility;
|
||||||
@ -178,7 +178,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
CSharpConsole = new CSConsolePanel();
|
CSharpConsole = new CSConsolePanel();
|
||||||
CSharpConsole.ConstructUI();
|
CSharpConsole.ConstructUI();
|
||||||
CSConsole.Init();
|
ConsoleController.Init();
|
||||||
|
|
||||||
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@ using UnityExplorer.UI.Panels;
|
|||||||
|
|
||||||
namespace UnityExplorer.UI.Widgets.AutoComplete
|
namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||||
{
|
{
|
||||||
|
// Shared modal panel for "AutoComplete" suggestions.
|
||||||
|
// A data source implements ISuggestionProvider and uses TakeOwnership and ReleaseOwnership
|
||||||
|
// for control, and SetSuggestions to set the actual suggestion data.
|
||||||
|
|
||||||
public class AutoCompleteModal : UIPanel
|
public class AutoCompleteModal : UIPanel
|
||||||
{
|
{
|
||||||
// Static
|
|
||||||
|
|
||||||
public static AutoCompleteModal Instance => UIManager.AutoCompleter;
|
public static AutoCompleteModal Instance => UIManager.AutoCompleter;
|
||||||
|
|
||||||
// Instance
|
|
||||||
|
|
||||||
public override string Name => "AutoCompleter";
|
public override string Name => "AutoCompleter";
|
||||||
public override UIManager.Panels PanelType => UIManager.Panels.AutoCompleter;
|
public override UIManager.Panels PanelType => UIManager.Panels.AutoCompleter;
|
||||||
public override int MinWidth => -1;
|
public override int MinWidth => -1;
|
||||||
@ -154,14 +154,14 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
|
|
||||||
if (CurrentHandler.AnchorToCaretPosition)
|
if (CurrentHandler.AnchorToCaretPosition)
|
||||||
{
|
{
|
||||||
var textGen = input.Component.textComponent.cachedTextGeneratorForLayout;
|
var textGen = input.Component.cachedInputTextGenerator;
|
||||||
int caretIdx = Math.Max(0, Math.Min(textGen.characterCount - 1, input.Component.caretPosition));
|
int caretIdx = Math.Max(0, Math.Min(textGen.characterCount - 1, input.Component.caretPosition));
|
||||||
|
|
||||||
// normalize the caret horizontal position
|
// normalize the caret horizontal position
|
||||||
Vector3 caretPos = textGen.characters[caretIdx].cursorPos;
|
Vector3 caretPos = textGen.characters[caretIdx].cursorPos;
|
||||||
caretPos += new Vector3(input.Rect.rect.width * 0.5f, 0, 0);
|
|
||||||
// transform to world point
|
// transform to world point
|
||||||
caretPos = input.UIRoot.transform.TransformPoint(caretPos);
|
caretPos = input.UIRoot.transform.TransformPoint(caretPos);
|
||||||
|
caretPos += new Vector3(input.Rect.rect.width * 0.5f, -(input.Rect.rect.height * 0.5f), 0);
|
||||||
|
|
||||||
uiRoot.transform.position = new Vector3(caretPos.x + 10, caretPos.y - 30, 0);
|
uiRoot.transform.position = new Vector3(caretPos.x + 10, caretPos.y - 30, 0);
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Core\Config\InternalConfigHandler.cs" />
|
<Compile Include="Core\Config\InternalConfigHandler.cs" />
|
||||||
<Compile Include="Core\Runtime\Mono\SortedSet.cs" />
|
|
||||||
<Compile Include="UI\CSConsole\CSAutoCompleter.cs" />
|
<Compile Include="UI\CSConsole\CSAutoCompleter.cs" />
|
||||||
<Compile Include="UI\CSConsole\LexerBuilder.cs" />
|
<Compile Include="UI\CSConsole\LexerBuilder.cs" />
|
||||||
<Compile Include="UI\CSConsole\Lexers\CommentLexer.cs" />
|
<Compile Include="UI\CSConsole\Lexers\CommentLexer.cs" />
|
||||||
@ -234,7 +233,7 @@
|
|||||||
<Compile Include="Core\Utility\MiscUtility.cs" />
|
<Compile Include="Core\Utility\MiscUtility.cs" />
|
||||||
<Compile Include="Core\Utility\ParseUtility.cs" />
|
<Compile Include="Core\Utility\ParseUtility.cs" />
|
||||||
<Compile Include="UI\Inspectors\TODO_InspectUnderMouse.cs" />
|
<Compile Include="UI\Inspectors\TODO_InspectUnderMouse.cs" />
|
||||||
<Compile Include="UI\CSConsole\CSConsole.cs" />
|
<Compile Include="UI\CSConsole\ConsoleController.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheField.cs" />
|
<Compile Include="UI\CacheObject\CacheField.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheKeyValuePair.cs" />
|
<Compile Include="UI\CacheObject\CacheKeyValuePair.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheListEntry.cs" />
|
<Compile Include="UI\CacheObject\CacheListEntry.cs" />
|
||||||
|
Reference in New Issue
Block a user