diff --git a/src/UI/CSConsole/ConsoleController.cs b/src/UI/CSConsole/ConsoleController.cs
index aba3c4d..3c88e1b 100644
--- a/src/UI/CSConsole/ConsoleController.cs
+++ b/src/UI/CSConsole/ConsoleController.cs
@@ -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
///
@@ -384,43 +397,83 @@ namespace UnityExplorer.UI.CSConsole
///
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;
}
diff --git a/src/UI/CSConsole/LexerBuilder.cs b/src/UI/CSConsole/LexerBuilder.cs
index 9e18997..4bf19d2 100644
--- a/src/UI/CSConsole/LexerBuilder.cs
+++ b/src/UI/CSConsole/LexerBuilder.cs
@@ -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
diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs
index a6a7846..0d21aaa 100644
--- a/src/UI/Panels/CSConsolePanel.cs
+++ b/src/UI/Panels/CSConsolePanel.cs
@@ -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(inputArea, false, true, true, true);
+ inputArea.AddComponent().color = Color.white;
+ inputArea.AddComponent().showMaskGraphic = false;
+
+ // line numbers
+
+ var linesHolder = UIFactory.CreateUIObject("LinesHolder", inputArea);
+ var linesRect = linesHolder.GetComponent();
+ 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().color = new Color(0.05f, 0.05f, 0.05f);
+ UIFactory.SetLayoutGroup(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);
}
}
}
diff --git a/src/UI/Widgets/InputFieldScroller.cs b/src/UI/Widgets/InputFieldScroller.cs
index 4fea90c..5233e7f 100644
--- a/src/UI/Widgets/InputFieldScroller.cs
+++ b/src/UI/Widgets/InputFieldScroller.cs
@@ -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();
}
}