Skip to content
This repository was archived by the owner on May 9, 2023. It is now read-only.

Commit 9b42eef

Browse files
committed
Line numbers and startup script
1 parent 830000b commit 9b42eef

File tree

4 files changed

+158
-44
lines changed

4 files changed

+158
-44
lines changed

src/UI/CSConsole/ConsoleController.cs

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public static class ConsoleController
3636
public static bool EnableAutoIndent { get; private set; } = true;
3737
public static bool EnableSuggestions { get; private set; } = true;
3838

39+
internal static string ScriptsFolder => Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Scripts");
40+
3941
internal static readonly string[] DefaultUsing = new string[]
4042
{
4143
"System",
@@ -56,6 +58,18 @@ public static void Init()
5658
ResetConsole(false);
5759
// ensure the compiler is supported (if this fails then SRE is probably stubbed)
5860
Evaluator.Compile("0 == 0");
61+
62+
if (!Directory.Exists(ScriptsFolder))
63+
Directory.CreateDirectory(ScriptsFolder);
64+
65+
var startupPath = Path.Combine(ScriptsFolder, "startup.cs");
66+
if (File.Exists(startupPath))
67+
{
68+
ExplorerCore.Log($"Executing startup script from '{startupPath}'...");
69+
var text = File.ReadAllText(startupPath);
70+
Input.Text = text;
71+
Evaluate();
72+
}
5973
}
6074
catch (Exception ex)
6175
{
@@ -69,7 +83,7 @@ public static void Init()
6983
SetupHelpInteraction();
7084

7185
Panel.OnInputChanged += OnInputChanged;
72-
Panel.InputScroll.OnScroll += OnInputScrolled;
86+
Panel.InputScroller.OnScroll += OnInputScrolled;
7387
Panel.OnCompileClicked += Evaluate;
7488
Panel.OnResetClicked += ResetConsole;
7589
Panel.OnHelpDropdownChanged += HelpSelected;
@@ -317,7 +331,7 @@ private static void UpdateCaret(out bool caretMoved)
317331
var charBot = charTop - CSCONSOLE_LINEHEIGHT;
318332

319333
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
320-
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
334+
var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
321335

322336
float diff = 0f;
323337
if (charTop > viewportMin)
@@ -337,7 +351,7 @@ private static void SetCaretPosition(int caretPosition)
337351
{
338352
settingCaretCoroutine = true;
339353
Input.Component.readOnly = true;
340-
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition));
354+
RuntimeProvider.Instance.StartCoroutine(SetCaretCoroutine(caretPosition));
341355
}
342356

343357
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
@@ -352,7 +366,7 @@ private static PropertyInfo GetSelectionGuardPropInfo()
352366

353367
private static PropertyInfo selectionGuardPropInfo;
354368

355-
private static IEnumerator SetAutocompleteCaretCoro(int caretPosition)
369+
private static IEnumerator SetCaretCoroutine(int caretPosition)
356370
{
357371
var color = Input.Component.selectionColor;
358372
color.a = 0f;
@@ -376,51 +390,90 @@ private static IEnumerator SetAutocompleteCaretCoro(int caretPosition)
376390
settingCaretCoroutine = false;
377391
}
378392

379-
380393
#region Lexer Highlighting
381394

382395
/// <summary>
383396
/// Returns true if caret is inside string or comment, false otherwise
384397
/// </summary>
385398
private static bool HighlightVisibleInput()
386399
{
387-
int startIdx = 0;
388-
int endIdx = Input.Text.Length - 1;
389-
int topLine = 0;
390-
391-
// Calculate visible text if necessary
392-
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
400+
if (string.IsNullOrEmpty(Input.Text))
393401
{
394-
topLine = -1;
395-
int bottomLine = -1;
402+
Panel.HighlightText.text = "";
403+
Panel.LineNumberText.text = "1";
404+
return false;
405+
}
396406

397-
// the top and bottom position of the viewport in relation to the text height
398-
// they need the half-height adjustment to normalize against the 'line.topY' value.
399-
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
400-
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
407+
// Calculate the visible lines
401408

402-
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
403-
{
404-
var line = Input.TextGenerator.lines[i];
405-
// if not set the top line yet, and top of line is below the viewport top
406-
if (topLine == -1 && line.topY <= viewportMin)
407-
topLine = i;
408-
// if bottom of line is below the viewport bottom
409-
if ((line.topY - line.height) >= viewportMax)
410-
bottomLine = i;
411-
}
409+
int topLine = -1;
410+
int bottomLine = -1;
412411

413-
topLine = Math.Max(0, topLine - 1);
414-
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
412+
// the top and bottom position of the viewport in relation to the text height
413+
// they need the half-height adjustment to normalize against the 'line.topY' value.
414+
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
415+
var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
415416

416-
startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
417-
endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
418-
? Input.Text.Length - 1
419-
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
417+
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
418+
{
419+
var line = Input.TextGenerator.lines[i];
420+
// if not set the top line yet, and top of line is below the viewport top
421+
if (topLine == -1 && line.topY <= viewportMin)
422+
topLine = i;
423+
// if bottom of line is below the viewport bottom
424+
if ((line.topY - line.height) >= viewportMax)
425+
bottomLine = i;
420426
}
421427

428+
topLine = Math.Max(0, topLine - 1);
429+
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
430+
431+
int startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
432+
int endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
433+
? Input.Text.Length - 1
434+
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
435+
436+
422437
// Highlight the visible text with the LexerBuilder
438+
423439
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret);
440+
441+
// Set the line numbers
442+
443+
// determine true starting line number (not the same as the cached TextGenerator line numbers)
444+
int realStartLine = 0;
445+
for (int i = 0; i < startIdx; i++)
446+
{
447+
if (LexerBuilder.IsNewLine(Input.Text[i]))
448+
realStartLine++;
449+
}
450+
realStartLine++;
451+
char lastPrev = '\n';
452+
453+
var sb = new StringBuilder();
454+
455+
// append leading new lines for spacing (no point rendering line numbers we cant see)
456+
for (int i = 0; i < topLine; i++)
457+
sb.Append('\n');
458+
459+
// append the displayed line numbers
460+
for (int i = topLine; i <= bottomLine; i++)
461+
{
462+
if (i > 0)
463+
lastPrev = Input.Text[Input.TextGenerator.lines[i].startCharIdx - 1];
464+
465+
// previous line ended with a newline character, this is an actual new line.
466+
if (LexerBuilder.IsNewLine(lastPrev))
467+
{
468+
sb.Append(realStartLine.ToString());
469+
realStartLine++;
470+
}
471+
472+
sb.Append('\n');
473+
}
474+
475+
Panel.LineNumberText.text = sb.ToString();
476+
424477
return ret;
425478
}
426479

src/UI/CSConsole/LexerBuilder.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ public struct MatchInfo
1313
{
1414
public int startIndex;
1515
public int endIndex;
16-
public string htmlColorTag;
1716
public bool isStringOrComment;
17+
public bool matchToEndOfLine;
18+
public string htmlColorTag;
1819
}
1920

2021
public class LexerBuilder
@@ -112,12 +113,24 @@ public string BuildHighlightedString(string input, int startIdx, int endIdx, int
112113
sb.Append(input[i]);
113114
sb.Append(SignatureHighlighter.CLOSE_COLOR);
114115

116+
// update the last unhighlighted start index
117+
lastUnhighlighted = match.endIndex + 1;
118+
119+
int matchEndIdx = match.endIndex;
120+
if (match.matchToEndOfLine)
121+
{
122+
while (input.Length - 1 >= matchEndIdx)
123+
{
124+
if (IsNewLine(input[matchEndIdx]))
125+
break;
126+
matchEndIdx++;
127+
}
128+
}
129+
115130
// check caretIdx to determine inStringOrComment state
116-
if (caretIdx >= match.startIndex && (caretIdx <= match.endIndex || (caretIdx >= input.Length && match.endIndex >= input.Length - 1)))
131+
if (caretIdx >= match.startIndex && (caretIdx <= matchEndIdx || (caretIdx >= input.Length && matchEndIdx >= input.Length - 1)))
117132
caretInStringOrComment = match.isStringOrComment;
118133

119-
// update the last unhighlighted start index
120-
lastUnhighlighted = match.endIndex + 1;
121134
}
122135

123136
// Append trailing unhighlighted input

src/UI/Panels/CSConsolePanel.cs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Text;
@@ -18,10 +19,11 @@ public class CSConsolePanel : UIPanel
1819
public override int MinWidth => 750;
1920
public override int MinHeight => 300;
2021

21-
public InputFieldScroller InputScroll { get; private set; }
22-
public InputFieldRef Input => InputScroll.InputField;
22+
public InputFieldScroller InputScroller { get; private set; }
23+
public InputFieldRef Input => InputScroller.InputField;
2324
public Text InputText { get; private set; }
2425
public Text HighlightText { get; private set; }
26+
public Text LineNumberText { get; private set; }
2527

2628
public Dropdown HelpDropdown { get; private set; }
2729

@@ -121,19 +123,53 @@ public override void ConstructPanelContent()
121123

122124
// Console Input
123125

126+
var inputArea = UIFactory.CreateUIObject("InputGroup", content);
127+
UIFactory.SetLayoutElement(inputArea, flexibleWidth: 9999, flexibleHeight: 9999);
128+
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(inputArea, false, true, true, true);
129+
inputArea.AddComponent<Image>().color = Color.white;
130+
inputArea.AddComponent<Mask>().showMaskGraphic = false;
131+
132+
// line numbers
133+
134+
var linesHolder = UIFactory.CreateUIObject("LinesHolder", inputArea);
135+
var linesRect = linesHolder.GetComponent<RectTransform>();
136+
linesRect.pivot = new Vector2(0, 1);
137+
linesRect.anchorMin = new Vector2(0, 0);
138+
linesRect.anchorMax = new Vector2(0, 1);
139+
linesRect.sizeDelta = new Vector2(0, 305000);
140+
linesRect.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 50);
141+
linesHolder.AddComponent<Image>().color = new Color(0.05f, 0.05f, 0.05f);
142+
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(linesHolder, true, true, true, true);
143+
144+
LineNumberText = UIFactory.CreateLabel(linesHolder, "LineNumbers", "1", TextAnchor.UpperCenter, Color.grey, fontSize: 16);
145+
LineNumberText.font = UIManager.ConsoleFont;
146+
147+
// input field
148+
124149
int fontSize = 16;
125150

126-
var inputObj = UIFactory.CreateScrollInputField(this.content, "ConsoleInput", ConsoleController.STARTUP_TEXT, out var inputScroller, fontSize);
127-
InputScroll = inputScroller;
151+
var inputObj = UIFactory.CreateScrollInputField(inputArea, "ConsoleInput", ConsoleController.STARTUP_TEXT,
152+
out var inputScroller, fontSize);
153+
InputScroller = inputScroller;
128154
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
129155
Input.OnValueChanged += InvokeOnValueChanged;
130156

157+
// move line number text with input field
158+
linesRect.transform.SetParent(inputObj.transform.Find("Viewport"), false);
159+
inputScroller.Slider.Scrollbar.onValueChanged.AddListener((float val) => { SetLinesPosition(); });
160+
inputScroller.Slider.Slider.onValueChanged.AddListener((float val) => { SetLinesPosition(); });
161+
void SetLinesPosition()
162+
{
163+
linesRect.anchoredPosition = new Vector2(linesRect.anchoredPosition.x, inputScroller.ContentRect.anchoredPosition.y);
164+
//SetInputLayout();
165+
}
166+
131167
InputText = Input.Component.textComponent;
132168
InputText.supportRichText = false;
133-
Input.PlaceholderText.fontSize = fontSize;
134169
InputText.color = Color.clear;
135170
Input.Component.customCaretColor = true;
136171
Input.Component.caretColor = Color.white;
172+
Input.PlaceholderText.fontSize = fontSize;
137173

138174
// Lexer highlight text overlay
139175
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", InputText.gameObject);
@@ -154,7 +190,19 @@ public override void ConstructPanelContent()
154190
Input.PlaceholderText.font = UIManager.ConsoleFont;
155191
HighlightText.font = UIManager.ConsoleFont;
156192

193+
RuntimeProvider.Instance.StartCoroutine(DelayedLayoutSetup());
194+
}
195+
196+
private IEnumerator DelayedLayoutSetup()
197+
{
198+
yield return null;
199+
SetInputLayout();
200+
}
157201

202+
public void SetInputLayout()
203+
{
204+
Input.Rect.offsetMin = new Vector2(52, Input.Rect.offsetMin.y);
205+
Input.Rect.offsetMax = new Vector2(2, Input.Rect.offsetMax.y);
158206
}
159207
}
160208
}

src/UI/Widgets/InputFieldScroller.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ public override void Update()
8080

8181
if (ContentRect.rect.height < desiredHeight)
8282
{
83-
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
83+
ContentRect.sizeDelta = new Vector2(ContentRect.sizeDelta.x, desiredHeight);
8484
this.Slider.UpdateSliderHandle();
8585
}
8686
else if (ContentRect.rect.height > desiredHeight)
8787
{
88-
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
88+
ContentRect.sizeDelta = new Vector2(ContentRect.sizeDelta.x, desiredHeight);
8989
this.Slider.UpdateSliderHandle();
9090
}
9191
}

0 commit comments

Comments
 (0)