1
0
mirror of https://github.com/originalnicodr/CinematicUnityExplorer.git synced 2026-10-10 21:13:15 +08:00

More progress on C# Console - implement AutoCompletes, some cleanups

This commit is contained in:
Sinai
2021-05-11 19:15:46 +10:00
parent 712bf7b669
commit 6e9bb83099
10 changed files with 269 additions and 160 deletions
+1 -1
View File
@@ -139,7 +139,7 @@ Building the project should be straight-forward, the references are all inside t
## Acknowledgments
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], snippets from the REPL Console were used for UnityExplorer's C# Console.
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], the ScriptEvaluator from RUE's REPL console was used for UnityExplorer's C# console.
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
* [HerpDerpenstine](https://github.com/HerpDerpinstine) for [MelonCoroutines](https://github.com/LavaGang/MelonLoader/blob/6cc958ec23b5e2e8453a73bc2e0d5aa353d4f0d1/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs) \[[license](THIRDPARTY_LICENSES.md#melonloader-license)\], they were included for standalone IL2CPP coroutine support.
+79
View File
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.CSharpConsole.Lexers;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.CSharpConsole
{
public class CSAutoCompleter : ISuggestionProvider
{
public InputFieldRef InputField => CSConsole.Input;
public bool AnchorToCaretPosition => true;
public void OnSuggestionClicked(Suggestion suggestion)
{
CSConsole.InsertSuggestionAtCaret(suggestion.UnderlyingValue);
}
private readonly HashSet<char> delimiters = new HashSet<char>
{
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
};
//private readonly HashSet<string> expressions = new HashSet<string>
//{
// "new",
// "is", "as",
// "return", "yield", "throw", "in",
// "do", "for", "foreach",
// "else",
//};
public void CheckAutocompletes()
{
if (string.IsNullOrEmpty(InputField.Text))
{
AutoCompleteModal.Instance.ReleaseOwnership(this);
return;
}
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
int i = caret;
while (i > 0)
{
i--;
char c = InputField.Text[i];
if (char.IsWhiteSpace(c) || delimiters.Contains(c))
{
i++;
break;
}
}
i = Math.Max(0, i);
string input = InputField.Text.Substring(i, (caret - i + 1));
string[] evaluatorCompletions = CSConsole.Evaluator.GetCompletions(input, out string prefix);
if (evaluatorCompletions != null && evaluatorCompletions.Any())
{
var suggestions = from completion in evaluatorCompletions
select new Suggestion($"<color=cyan>{prefix}</color>{completion}", completion);
AutoCompleteModal.Instance.TakeOwnership(this);
AutoCompleteModal.Instance.SetSuggestions(suggestions);
}
else
{
AutoCompleteModal.Instance.ReleaseOwnership(this);
}
}
}
}
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -13,7 +13,9 @@ namespace UnityExplorer.UI.CSharpConsole.Lexers
// all symbols are delimiters
public override IEnumerable<char> Delimiters => symbols;
private readonly HashSet<char> symbols = new HashSet<char>
public static bool IsSymbol(char c) => symbols.Contains(c);
public static readonly HashSet<char> symbols = new HashSet<char>
{
'[', '{', '(', // open
']', '}', ')', // close
@@ -29,14 +31,14 @@ namespace UnityExplorer.UI.CSharpConsole.Lexers
if (!lexer.IsDelimiter(lexer.Previous, true, true))
return false;
if (symbols.Contains(lexer.Current))
if (IsSymbol(lexer.Current))
{
do
{
lexer.Commit();
lexer.PeekNext();
}
while (symbols.Contains(lexer.Current));
while (IsSymbol(lexer.Current));
return true;
}
+11 -3
View File
@@ -4,7 +4,7 @@ using System.IO;
using System.Reflection;
using Mono.CSharp;
// Thanks to ManlyMarco for most of this
// Thanks to ManlyMarco for this
namespace UnityExplorer.Core.CSharp
{
@@ -22,7 +22,7 @@ namespace UnityExplorer.Core.CSharp
{
_textWriter = tw;
ImportAppdomainAssemblies(ReferenceAssembly);
ImportAppdomainAssemblies(Reference);
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
@@ -39,7 +39,15 @@ namespace UnityExplorer.Core.CSharp
if (StdLib.Contains(name))
return;
ReferenceAssembly(args.LoadedAssembly);
Reference(args.LoadedAssembly);
}
private void Reference(Assembly asm)
{
var name = asm.GetName().Name;
if (name == "completions")
return;
ReferenceAssembly(asm);
}
private static CompilerContext BuildContext(TextWriter tw)
+29 -35
View File
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.Core.CSharp
namespace UnityExplorer.UI.CSharpConsole
{
public class ScriptInteraction : InteractiveBase
{
@@ -20,45 +20,39 @@ namespace UnityExplorer.Core.CSharp
RuntimeProvider.Instance.StartCoroutine(ienumerator);
}
//public static void AddUsing(string directive)
//{
// CSharpConsole.Instance.AddUsing(directive);
//}
public static void AddUsing(string directive)
{
CSConsole.AddUsing(directive);
}
//public static void GetUsing()
//{
// ExplorerCore.Log(CSharpConsole.Instance.Evaluator.GetUsing());
//}
public static void GetUsing()
{
ExplorerCore.Log(CSConsole.Evaluator.GetUsing());
}
//public static void Reset()
//{
// CSharpConsole.Instance.ResetConsole();
//}
public static void Reset()
{
CSConsole.ResetConsole();
}
//public static object CurrentTarget()
//{
// return InspectorManager.Instance?.m_activeInspector?.Target;
//}
public static object CurrentTarget()
{
return InspectorManager.ActiveInspector?.Target;
}
//public static object[] AllTargets()
//{
// int count = InspectorManager.Instance?.m_currentInspectors.Count ?? 0;
// object[] ret = new object[count];
// for (int i = 0; i < count; i++)
// {
// ret[i] = InspectorManager.Instance?.m_currentInspectors[i].Target;
// }
// return ret;
//}
public static object[] AllTargets()
{
return InspectorManager.Inspectors.Select(it => it.Target).ToArray();
}
//public static void Inspect(object obj)
//{
// InspectorManager.Instance.Inspect(obj);
//}
public static void Inspect(object obj)
{
InspectorManager.Inspect(obj);
}
//public static void Inspect(Type type)
//{
// InspectorManager.Instance.Inspect(type);
//}
public static void Inspect(Type type)
{
InspectorManager.Inspect(type);
}
}
}
+7 -7
View File
@@ -76,10 +76,10 @@ namespace UnityExplorer.UI.Panels
default, TextAnchor.LowerCenter);
UIFactory.SetLayoutElement(topBarObj, minHeight: 50, flexibleHeight: 0);
// Top label
//// Top label
var topBarLabel = UIFactory.CreateLabel(topBarObj, "TopLabel", "C# Console", TextAnchor.MiddleLeft, default, true, 25);
UIFactory.SetLayoutElement(topBarLabel.gameObject, preferredWidth: 150, flexibleWidth: 5000);
//var topBarLabel = UIFactory.CreateLabel(topBarObj, "TopLabel", "C# Console", TextAnchor.MiddleLeft, default, true, 25);
//UIFactory.SetLayoutElement(topBarLabel.gameObject, preferredWidth: 150, flexibleWidth: 5000);
// Enable Ctrl+R toggle
@@ -113,10 +113,10 @@ namespace UnityExplorer.UI.Panels
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsole.STARTUP_TEXT, out var inputScroller, fontSize);
InputScroll = inputScroller;
CSConsole.defaultInputFieldAlpha = Input.InputField.selectionColor.a;
CSConsole.defaultInputFieldAlpha = Input.Component.selectionColor.a;
Input.OnValueChanged += InvokeOnValueChanged;
InputText = Input.InputField.textComponent;
InputText = Input.Component.textComponent;
InputText.supportRichText = false;
InputText.color = Color.white;
Input.PlaceholderText.fontSize = fontSize;
@@ -148,12 +148,12 @@ namespace UnityExplorer.UI.Panels
new Color(1, 1, 1, 0));
var resetButton = UIFactory.CreateButton(horozGroupObj, "ResetButton", "Reset", new Color(0.33f, 0.33f, 0.33f));
UIFactory.SetLayoutElement(resetButton.Button.gameObject, minHeight: 45, minWidth: 80, flexibleHeight: 0);
UIFactory.SetLayoutElement(resetButton.Component.gameObject, minHeight: 45, minWidth: 80, flexibleHeight: 0);
resetButton.ButtonText.fontSize = 18;
resetButton.OnClick += OnResetClicked;
var compileButton = UIFactory.CreateButton(horozGroupObj, "CompileButton", "Compile", new Color(0.33f, 0.5f, 0.33f));
UIFactory.SetLayoutElement(compileButton.Button.gameObject, minHeight: 45, minWidth: 80, flexibleHeight: 0);
UIFactory.SetLayoutElement(compileButton.Component.gameObject, minHeight: 45, minWidth: 80, flexibleHeight: 0);
compileButton.ButtonText.fontSize = 18;
compileButton.OnClick += OnCompileClicked;
@@ -12,11 +12,11 @@ using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets.AutoComplete
{
public class AutoCompleter : UIPanel
public class AutoCompleteModal : UIPanel
{
// Static
public static AutoCompleter Instance => UIManager.AutoCompleter;
public static AutoCompleteModal Instance => UIManager.AutoCompleter;
// Instance
@@ -36,9 +36,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
private List<Suggestion> suggestions = new List<Suggestion>();
private int lastCaretPos;
public AutoCompleter()
public AutoCompleteModal()
{
OnPanelsReordered += UIPanel_OnPanelsReordered;
OnClickedOutsidePanels += AutoCompleter_OnClickedOutsidePanels;
@@ -80,7 +78,6 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
ReleaseOwnership(CurrentHandler);
else
{
lastCaretPos = CurrentHandler.InputField.InputField.caretPosition;
UpdatePosition();
}
}
@@ -107,9 +104,9 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
private bool ShouldDisplay(Suggestion data, string filter) => true;
public void SetSuggestions(List<Suggestion> collection)
public void SetSuggestions(IEnumerable<Suggestion> collection)
{
suggestions = collection;
suggestions = collection as List<Suggestion> ?? collection.ToList();
if (!suggestions.Any())
UIRoot.SetActive(false);
@@ -140,28 +137,40 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
cell.Button.ButtonText.text = suggestion.DisplayText;
}
private int lastCaretPosition;
private Vector3 lastInputPosition;
private void UpdatePosition()
{
if (CurrentHandler == null || !CurrentHandler.InputField.InputField.isFocused)
if (CurrentHandler == null || !CurrentHandler.InputField.Component.isFocused)
return;
Vector3 pos;
var input = CurrentHandler.InputField;
var textGen = input.InputField.textComponent.cachedTextGenerator;
int caretPos = 0;
if (input.Component.caretPosition == lastCaretPosition && input.UIRoot.transform.position == lastInputPosition)
return;
lastInputPosition = input.UIRoot.transform.position;
lastCaretPosition = input.Component.caretPosition;
if (CurrentHandler.AnchorToCaretPosition)
{
caretPos = lastCaretPos--;
var textGen = input.Component.textComponent.cachedTextGeneratorForLayout;
int caretIdx = Math.Max(0, Math.Min(textGen.characterCount - 1, input.Component.caretPosition));
caretPos = Math.Max(0, caretPos);
caretPos = Math.Min(textGen.characterCount - 1, caretPos);
// normalize the caret horizontal position
Vector3 caretPos = textGen.characters[caretIdx].cursorPos;
caretPos += new Vector3(input.Rect.rect.width * 0.5f, 0, 0);
// transform to world point
caretPos = input.UIRoot.transform.TransformPoint(caretPos);
uiRoot.transform.position = new Vector3(caretPos.x + 10, caretPos.y - 30, 0);
}
else
{
var textGen = input.Component.textComponent.cachedTextGenerator;
var pos = input.UIRoot.transform.TransformPoint(textGen.characters[0].cursorPos);
uiRoot.transform.position = new Vector3(pos.x + 10, pos.y - 20, 0);
}
pos = textGen.characters[caretPos].cursorPos;
pos = input.UIRoot.transform.TransformPoint(pos);
uiRoot.transform.position = new Vector3(pos.x + 10, pos.y - 20, 0);
this.Dragger.OnEndResize();
}
+4 -4
View File
@@ -53,7 +53,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
SuggestionClicked?.Invoke(suggestion);
suggestions.Clear();
AutoCompleter.Instance.SetSuggestions(suggestions);
AutoCompleteModal.Instance.SetSuggestions(suggestions);
chosenSuggestion = suggestion.UnderlyingValue;
}
@@ -62,14 +62,14 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
if (string.IsNullOrEmpty(value) || value == chosenSuggestion)
{
chosenSuggestion = null;
AutoCompleter.Instance.ReleaseOwnership(this);
AutoCompleteModal.Instance.ReleaseOwnership(this);
}
else
{
GetSuggestions(value);
AutoCompleter.Instance.TakeOwnership(this);
AutoCompleter.Instance.SetSuggestions(suggestions);
AutoCompleteModal.Instance.TakeOwnership(this);
AutoCompleteModal.Instance.SetSuggestions(suggestions);
}
}
+4 -4
View File
@@ -105,23 +105,23 @@ namespace UnityExplorer.UI.Utility
internal void ProcessInputText()
{
var curInputRect = InputField.InputField.textComponent.rectTransform.rect;
var curInputRect = InputField.Component.textComponent.rectTransform.rect;
var scaleFactor = RootScaler.scaleFactor;
// Current text settings
var texGenSettings = InputField.InputField.textComponent.GetGenerationSettings(curInputRect.size);
var texGenSettings = InputField.Component.textComponent.GetGenerationSettings(curInputRect.size);
texGenSettings.generateOutOfBounds = false;
texGenSettings.scaleFactor = scaleFactor;
// Preferred text rect height
var textGen = InputField.InputField.textComponent.cachedTextGeneratorForLayout;
var textGen = InputField.Component.textComponent.cachedTextGeneratorForLayout;
m_desiredContentHeight = textGen.GetPreferredHeight(m_lastText, texGenSettings) + 10;
// TODO more intelligent jump.
// We can detect if the caret is outside the viewport area.
// jump to bottom
if (InputField.InputField.caretPosition == InputField.Text.Length
if (InputField.Component.caretPosition == InputField.Text.Length
&& InputField.Text.Length > 0
&& InputField.Text[InputField.Text.Length - 1] == '\n')
{