Compare commits

..

3 Commits
4.1.5 ... 4.1.6

Author SHA1 Message Date
7a59f9a2a1 Update info about startup script -noci 2021-06-20 19:10:44 +10:00
9b42eef1b9 Line numbers and startup script 2021-06-20 19:06:52 +10:00
830000b019 Bump version 2021-06-20 19:06:33 +10:00
6 changed files with 168 additions and 51 deletions

View File

@ -91,6 +91,7 @@ The inspector is used to see detailed information on objects of any type and man
### C# Console
* The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
* You can execute a script automatically on startup by naming it `startup.cs` and placing it in the `UnityExplorer\Scripts\` folder (this folder will be created where you placed the DLL file).
* See the "Help" dropdown in the C# console menu for more detailed information.
### Mouse-Inspect

View File

@ -20,7 +20,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "4.1.5";
public const string VERSION = "4.1.6";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";

View File

@ -36,6 +36,8 @@ namespace UnityExplorer.UI.CSConsole
public static bool EnableAutoIndent { get; private set; } = true;
public static bool EnableSuggestions { get; private set; } = true;
internal static string ScriptsFolder => Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Scripts");
internal static readonly string[] DefaultUsing = new string[]
{
"System",
@ -56,6 +58,18 @@ namespace UnityExplorer.UI.CSConsole
ResetConsole(false);
// ensure the compiler is supported (if this fails then SRE is probably stubbed)
Evaluator.Compile("0 == 0");
if (!Directory.Exists(ScriptsFolder))
Directory.CreateDirectory(ScriptsFolder);
var startupPath = Path.Combine(ScriptsFolder, "startup.cs");
if (File.Exists(startupPath))
{
ExplorerCore.Log($"Executing startup script from '{startupPath}'...");
var text = File.ReadAllText(startupPath);
Input.Text = text;
Evaluate();
}
}
catch (Exception ex)
{
@ -69,7 +83,7 @@ namespace UnityExplorer.UI.CSConsole
SetupHelpInteraction();
Panel.OnInputChanged += OnInputChanged;
Panel.InputScroll.OnScroll += OnInputScrolled;
Panel.InputScroller.OnScroll += OnInputScrolled;
Panel.OnCompileClicked += Evaluate;
Panel.OnResetClicked += ResetConsole;
Panel.OnHelpDropdownChanged += HelpSelected;
@ -317,7 +331,7 @@ namespace UnityExplorer.UI.CSConsole
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;
var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
float diff = 0f;
if (charTop > viewportMin)
@ -337,7 +351,7 @@ namespace UnityExplorer.UI.CSConsole
{
settingCaretCoroutine = true;
Input.Component.readOnly = true;
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition));
RuntimeProvider.Instance.StartCoroutine(SetCaretCoroutine(caretPosition));
}
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
@ -352,7 +366,7 @@ namespace UnityExplorer.UI.CSConsole
private static PropertyInfo selectionGuardPropInfo;
private static IEnumerator SetAutocompleteCaretCoro(int caretPosition)
private static IEnumerator SetCaretCoroutine(int caretPosition)
{
var color = Input.Component.selectionColor;
color.a = 0f;
@ -376,7 +390,6 @@ namespace UnityExplorer.UI.CSConsole
settingCaretCoroutine = false;
}
#region Lexer Highlighting
/// <summary>
@ -384,43 +397,83 @@ namespace UnityExplorer.UI.CSConsole
/// </summary>
private static bool HighlightVisibleInput()
{
int startIdx = 0;
int endIdx = Input.Text.Length - 1;
int topLine = 0;
// Calculate visible text if necessary
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
if (string.IsNullOrEmpty(Input.Text))
{
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);
Panel.HighlightText.text = "";
Panel.LineNumberText.text = "1";
return false;
}
// Calculate the visible lines
int 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.InputScroller.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);
int startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
int endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
? Input.Text.Length - 1
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
// Highlight the visible text with the LexerBuilder
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret);
// Set the line numbers
// determine true starting line number (not the same as the cached TextGenerator line numbers)
int realStartLine = 0;
for (int i = 0; i < startIdx; i++)
{
if (LexerBuilder.IsNewLine(Input.Text[i]))
realStartLine++;
}
realStartLine++;
char lastPrev = '\n';
var sb = new StringBuilder();
// append leading new lines for spacing (no point rendering line numbers we cant see)
for (int i = 0; i < topLine; i++)
sb.Append('\n');
// append the displayed line numbers
for (int i = topLine; i <= bottomLine; i++)
{
if (i > 0)
lastPrev = Input.Text[Input.TextGenerator.lines[i].startCharIdx - 1];
// previous line ended with a newline character, this is an actual new line.
if (LexerBuilder.IsNewLine(lastPrev))
{
sb.Append(realStartLine.ToString());
realStartLine++;
}
sb.Append('\n');
}
Panel.LineNumberText.text = sb.ToString();
return ret;
}
@ -555,7 +608,9 @@ If the game was built with Unity's stubbed netstandard 2.0 runtime, you can fix
internal const string STARTUP_TEXT = @"<color=#5d8556>// Welcome to the UnityExplorer C# Console!
// It is recommended to use the Log panel (or a console log window) while using this tool.
// Use the Help dropdown to see detailed examples of how to use the console.</color>";
// Use the Help dropdown to see detailed examples of how to use the console.
// To execute a script automatically on startup, put the script at 'UnityExplorer\Scripts\startup.cs'</color>";
internal const string HELP_USINGS = @"// You can add a using directive to any namespace, but you must compile for it to take effect.
// It will remain in effect until you Reset the console.

View File

@ -13,8 +13,9 @@ namespace UnityExplorer.UI.CSConsole
{
public int startIndex;
public int endIndex;
public string htmlColorTag;
public bool isStringOrComment;
public bool matchToEndOfLine;
public string htmlColorTag;
}
public class LexerBuilder
@ -112,12 +113,24 @@ namespace UnityExplorer.UI.CSConsole
sb.Append(input[i]);
sb.Append(SignatureHighlighter.CLOSE_COLOR);
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= match.endIndex || (caretIdx >= input.Length && match.endIndex >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
// update the last unhighlighted start index
lastUnhighlighted = match.endIndex + 1;
int matchEndIdx = match.endIndex;
if (match.matchToEndOfLine)
{
while (input.Length - 1 >= matchEndIdx)
{
if (IsNewLine(input[matchEndIdx]))
break;
matchEndIdx++;
}
}
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= matchEndIdx || (caretIdx >= input.Length && matchEndIdx >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
}
// Append trailing unhighlighted input

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -18,10 +19,11 @@ namespace UnityExplorer.UI.Panels
public override int MinWidth => 750;
public override int MinHeight => 300;
public InputFieldScroller InputScroll { get; private set; }
public InputFieldRef Input => InputScroll.InputField;
public InputFieldScroller InputScroller { get; private set; }
public InputFieldRef Input => InputScroller.InputField;
public Text InputText { get; private set; }
public Text HighlightText { get; private set; }
public Text LineNumberText { get; private set; }
public Dropdown HelpDropdown { get; private set; }
@ -121,19 +123,53 @@ namespace UnityExplorer.UI.Panels
// Console Input
var inputArea = UIFactory.CreateUIObject("InputGroup", content);
UIFactory.SetLayoutElement(inputArea, flexibleWidth: 9999, flexibleHeight: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(inputArea, false, true, true, true);
inputArea.AddComponent<Image>().color = Color.white;
inputArea.AddComponent<Mask>().showMaskGraphic = false;
// line numbers
var linesHolder = UIFactory.CreateUIObject("LinesHolder", inputArea);
var linesRect = linesHolder.GetComponent<RectTransform>();
linesRect.pivot = new Vector2(0, 1);
linesRect.anchorMin = new Vector2(0, 0);
linesRect.anchorMax = new Vector2(0, 1);
linesRect.sizeDelta = new Vector2(0, 305000);
linesRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 50);
linesHolder.AddComponent<Image>().color = new Color(0.05f, 0.05f, 0.05f);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(linesHolder, true, true, true, true);
LineNumberText = UIFactory.CreateLabel(linesHolder, "LineNumbers", "1", TextAnchor.UpperCenter, Color.grey, fontSize: 16);
LineNumberText.font = UIManager.ConsoleFont;
// input field
int fontSize = 16;
var inputObj = UIFactory.CreateScrollInputField(this.content, "ConsoleInput", ConsoleController.STARTUP_TEXT, out var inputScroller, fontSize);
InputScroll = inputScroller;
var inputObj = UIFactory.CreateScrollInputField(inputArea, "ConsoleInput", ConsoleController.STARTUP_TEXT,
out var inputScroller, fontSize);
InputScroller = inputScroller;
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
Input.OnValueChanged += InvokeOnValueChanged;
// move line number text with input field
linesRect.transform.SetParent(inputObj.transform.Find("Viewport"), false);
inputScroller.Slider.Scrollbar.onValueChanged.AddListener((float val) => { SetLinesPosition(); });
inputScroller.Slider.Slider.onValueChanged.AddListener((float val) => { SetLinesPosition(); });
void SetLinesPosition()
{
linesRect.anchoredPosition = new Vector2(linesRect.anchoredPosition.x, inputScroller.ContentRect.anchoredPosition.y);
//SetInputLayout();
}
InputText = Input.Component.textComponent;
InputText.supportRichText = false;
Input.PlaceholderText.fontSize = fontSize;
InputText.color = Color.clear;
Input.Component.customCaretColor = true;
Input.Component.caretColor = Color.white;
Input.PlaceholderText.fontSize = fontSize;
// Lexer highlight text overlay
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", InputText.gameObject);
@ -154,7 +190,19 @@ namespace UnityExplorer.UI.Panels
Input.PlaceholderText.font = UIManager.ConsoleFont;
HighlightText.font = UIManager.ConsoleFont;
RuntimeProvider.Instance.StartCoroutine(DelayedLayoutSetup());
}
private IEnumerator DelayedLayoutSetup()
{
yield return null;
SetInputLayout();
}
public void SetInputLayout()
{
Input.Rect.offsetMin = new Vector2(52, Input.Rect.offsetMin.y);
Input.Rect.offsetMax = new Vector2(2, Input.Rect.offsetMax.y);
}
}
}

View File

@ -80,12 +80,12 @@ namespace UnityExplorer.UI.Widgets
if (ContentRect.rect.height < desiredHeight)
{
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
ContentRect.sizeDelta = new Vector2(ContentRect.sizeDelta.x, desiredHeight);
this.Slider.UpdateSliderHandle();
}
else if (ContentRect.rect.height > desiredHeight)
{
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
ContentRect.sizeDelta = new Vector2(ContentRect.sizeDelta.x, desiredHeight);
this.Slider.UpdateSliderHandle();
}
}