From caad39bb9af3ec7a11bee2320c9993bf6ef79f02 Mon Sep 17 00:00:00 2001 From: Sinai Date: Mon, 10 May 2021 15:58:49 +1000 Subject: [PATCH] Rewrite lexer from scratch --- README.md | 1 - THIRDPARTY_LICENSES.md | 193 -------------- src/UI/CSConsole/CSConsole.cs | 66 ++--- src/UI/CSConsole/CSLexer.cs | 355 ------------------------- src/UI/CSConsole/Lexer/CommentLexer.cs | 52 ++++ src/UI/CSConsole/Lexer/CommentMatch.cs | 48 ---- src/UI/CSConsole/Lexer/KeywordLexer.cs | 61 +++++ src/UI/CSConsole/Lexer/KeywordMatch.cs | 91 ------- src/UI/CSConsole/Lexer/Lexer.cs | 18 ++ src/UI/CSConsole/Lexer/Matcher.cs | 31 --- src/UI/CSConsole/Lexer/NumberLexer.cs | 31 +++ src/UI/CSConsole/Lexer/NumberMatch.cs | 39 --- src/UI/CSConsole/Lexer/StringLexer.cs | 59 ++++ src/UI/CSConsole/Lexer/StringMatch.cs | 26 -- src/UI/CSConsole/Lexer/SymbolLexer.cs | 52 ++++ src/UI/CSConsole/Lexer/SymbolMatch.cs | 98 ------- src/UI/CSConsole/LexerBuilder.cs | 347 ++++++++++++++++++++++++ src/UI/Panels/CSConsolePanel.cs | 3 +- src/UI/UIManager.cs | 2 - src/UnityExplorer.csproj | 14 +- 20 files changed, 650 insertions(+), 937 deletions(-) delete mode 100644 src/UI/CSConsole/CSLexer.cs create mode 100644 src/UI/CSConsole/Lexer/CommentLexer.cs delete mode 100644 src/UI/CSConsole/Lexer/CommentMatch.cs create mode 100644 src/UI/CSConsole/Lexer/KeywordLexer.cs delete mode 100644 src/UI/CSConsole/Lexer/KeywordMatch.cs create mode 100644 src/UI/CSConsole/Lexer/Lexer.cs delete mode 100644 src/UI/CSConsole/Lexer/Matcher.cs create mode 100644 src/UI/CSConsole/Lexer/NumberLexer.cs delete mode 100644 src/UI/CSConsole/Lexer/NumberMatch.cs create mode 100644 src/UI/CSConsole/Lexer/StringLexer.cs delete mode 100644 src/UI/CSConsole/Lexer/StringMatch.cs create mode 100644 src/UI/CSConsole/Lexer/SymbolLexer.cs delete mode 100644 src/UI/CSConsole/Lexer/SymbolMatch.cs create mode 100644 src/UI/CSConsole/LexerBuilder.cs diff --git a/README.md b/README.md index 41ed3e7..744d8aa 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,6 @@ Building the project should be straight-forward, the references are all inside t * [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. * [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. -* [InGameCodeEditor](https://assetstore.unity.com/packages/tools/gui/ingame-code-editor-144254) \[[license](THIRDPARTY_LICENSES.md#ingamecodeeditor-license)\] was used as the base for the syntax highlighting for UnityExplorer's C# console (`UnityExplorer.UI.Main.CSConsole.Lexer`). ### Disclaimer diff --git a/THIRDPARTY_LICENSES.md b/THIRDPARTY_LICENSES.md index 51f1514..78877a8 100644 --- a/THIRDPARTY_LICENSES.md +++ b/THIRDPARTY_LICENSES.md @@ -1,6 +1,5 @@ * [RuntimeUnityEditor License](#runtimeunityeditor-license) * [MelonLoader License](#melonloader-license) -* [InGameCodeEditor License](#ingamecodeeditor-license) ## RuntimeUnityEditor License @@ -873,195 +872,3 @@ Public License instead of this License. But first, please read See the License for the specific language governing permissions and limitations under the License. -## InGameCodeEditor License - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2020 - 2021 Lava Gang - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/UI/CSConsole/CSConsole.cs b/src/UI/CSConsole/CSConsole.cs index edc659b..cafe76c 100644 --- a/src/UI/CSConsole/CSConsole.cs +++ b/src/UI/CSConsole/CSConsole.cs @@ -55,7 +55,7 @@ The following helper methods are available: #endregion public static ScriptEvaluator Evaluator; - public static CSLexer Lexer; + public static LexerBuilder Lexer; private static StringBuilder evaluatorOutput; private static HashSet usingDirectives; @@ -72,7 +72,7 @@ The following helper methods are available: { try { - Lexer = new CSLexer(); + Lexer = new LexerBuilder(); ResetConsole(false); Evaluator.Compile("0 == 0"); @@ -173,8 +173,8 @@ The following helper methods are available: } } - if (EnableAutoIndent && InputManager.GetKeyDown(KeyCode.Return)) - DoAutoIndent(); + //if (EnableAutoIndent && InputManager.GetKeyDown(KeyCode.Return)) + // DoAutoIndent(); //if (EnableAutocompletes && InputField.isFocused) //{ @@ -183,55 +183,31 @@ The following helper methods are available: //} } + // Invoked at most once per frame private static void OnConsoleInputChanged(string input) { // todo update auto completes - // todo update syntax highlight + // update syntax highlight + Panel.HighlightText.text = Lexer.SyntaxHighlight(input); } + // 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); + //} + // Autocompletes - private static string SyntaxHighlight(string input) - { - var sb = new StringBuilder(); - int curIdx = 0; - foreach (var match in Lexer.GetMatches(input)) - { - // append non-highlighted text between last match and this - for (int i = curIdx; i < match.startIndex; i++) - sb.Append(input[i]); - - // append the highlighted match - sb.Append(match.htmlColorTag); - - for (int i = match.startIndex; i < match.endIndex; i++) - sb.Append(input[i]); - - sb.Append(SignatureHighlighter.CLOSE_COLOR); - - // update the index - curIdx = match.endIndex; - } - - return sb.ToString(); - } - - // Indent - - 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); - } - } } diff --git a/src/UI/CSConsole/CSLexer.cs b/src/UI/CSConsole/CSLexer.cs deleted file mode 100644 index 0d060f1..0000000 --- a/src/UI/CSConsole/CSLexer.cs +++ /dev/null @@ -1,355 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine.UI; -using UnityExplorer.UI.CSharpConsole.Lexer; - -namespace UnityExplorer.UI.CSharpConsole -{ - public struct LexerMatchInfo - { - public int startIndex; - public int endIndex; - public string htmlColorTag; - } - - public enum DelimiterType - { - Start, - End, - }; - - public class CSLexer - { - private string inputString; - private readonly Matcher[] matchers; - private readonly HashSet startDelimiters; - private readonly HashSet endDelimiters; - private int currentIndex; - private int currentLookaheadIndex; - - public char Current { get; private set; } - public char Previous { get; private set; } - - public bool EndOfStream => currentLookaheadIndex >= inputString.Length; - - public static char indentOpen = '{'; - public static char indentClose = '}'; - //private static StringBuilder indentBuilder = new StringBuilder(); - - public static char[] delimiters = new[] - { - '[', ']', '(', ')', '{', '}', ';', ':', ',', '.' - }; - - private static readonly CommentMatch commentMatcher = new CommentMatch(); - private static readonly SymbolMatch symbolMatcher = new SymbolMatch(); - private static readonly NumberMatch numberMatcher = new NumberMatch(); - private static readonly StringMatch stringMatcher = new StringMatch(); - private static readonly KeywordMatch keywordMatcher = new KeywordMatch(); - - public CSLexer() - { - startDelimiters = new HashSet(delimiters); - endDelimiters = new HashSet(delimiters); - - this.matchers = new Matcher[] - { - commentMatcher, - symbolMatcher, - numberMatcher, - stringMatcher, - keywordMatcher, - }; - - foreach (Matcher lexer in matchers) - { - foreach (char c in lexer.StartChars) - { - if (!startDelimiters.Contains(c)) - startDelimiters.Add(c); - } - - foreach (char c in lexer.EndChars) - { - if (!endDelimiters.Contains(c)) - endDelimiters.Add(c); - } - } - } - - public IEnumerable GetMatches(string input) - { - if (input == null || matchers == null || matchers.Length == 0) - yield break; - - inputString = input; - Current = ' '; - Previous = ' '; - currentIndex = 0; - currentLookaheadIndex = 0; - - while (!EndOfStream) - { - bool didMatchLexer = false; - - ReadWhiteSpace(); - - foreach (Matcher matcher in matchers) - { - int startIndex = currentIndex; - - bool isMatched = matcher.IsMatch(this); - - if (isMatched) - { - int endIndex = currentIndex; - - didMatchLexer = true; - - yield return new LexerMatchInfo - { - startIndex = startIndex, - endIndex = endIndex, - htmlColorTag = matcher.HexColorTag, - }; - - break; - } - } - - if (!didMatchLexer) - { - ReadNext(); - Commit(); - } - } - } - - // Lexer reading - - public char ReadNext() - { - if (EndOfStream) - return '\0'; - - Previous = Current; - - Current = inputString[currentLookaheadIndex]; - currentLookaheadIndex++; - - return Current; - } - - public void Rollback(int amount = -1) - { - if (amount == -1) - currentLookaheadIndex = currentIndex; - else - { - if (currentLookaheadIndex > currentIndex) - currentLookaheadIndex -= amount; - } - - int previousIndex = currentLookaheadIndex - 1; - - if (previousIndex >= inputString.Length) - Previous = inputString[inputString.Length - 1]; - else if (previousIndex >= 0) - Previous = inputString[previousIndex]; - else - Previous = ' '; - } - - public void Commit() - { - currentIndex = currentLookaheadIndex; - } - - public bool IsSpecialSymbol(char character, DelimiterType position = DelimiterType.Start) - { - if (position == DelimiterType.Start) - return startDelimiters.Contains(character); - - return endDelimiters.Contains(character); - } - - private void ReadWhiteSpace() - { - while (char.IsWhiteSpace(ReadNext()) == true) - Commit(); - - Rollback(); - } - - // 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 == indentOpen) - indent++; - else if (!stringState && character == indentClose) - 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 == indentClose) - 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 == indentClose || c == indentOpen) && !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 == indentOpen) - 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 == indentOpen) - currentIndent++; - else if (c == indentClose) - 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(); - } - } -} diff --git a/src/UI/CSConsole/Lexer/CommentLexer.cs b/src/UI/CSConsole/Lexer/CommentLexer.cs new file mode 100644 index 0000000..24dfbe9 --- /dev/null +++ b/src/UI/CSConsole/Lexer/CommentLexer.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace UnityExplorer.UI.CSharpConsole.Lexers +{ + public class CommentLexer : Lexer + { + private enum CommentType + { + Line, + Block + } + + protected override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f); + + public override bool TryMatchCurrent(LexerBuilder lexer) + { + if (lexer.Current == '/') + { + lexer.PeekNext(); + if (lexer.Current == '/') + { + // line comment. read to end of line or file. + do + { + lexer.Commit(); + lexer.PeekNext(); + } + while (!lexer.EndOrNewLine); + + return true; + } + else if (lexer.Current == '*') + { + // block comment, read until end of file or closing '*/' + lexer.PeekNext(); + do + { + lexer.PeekNext(); + lexer.Commit(); + } + while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*')); + + return true; + } + } + + return false; + } + } +} diff --git a/src/UI/CSConsole/Lexer/CommentMatch.cs b/src/UI/CSConsole/Lexer/CommentMatch.cs deleted file mode 100644 index d888934..0000000 --- a/src/UI/CSConsole/Lexer/CommentMatch.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace UnityExplorer.UI.CSharpConsole.Lexer -{ - public class CommentMatch : Matcher - { - public string lineCommentStart = @"//"; - public string blockCommentStart = @"/*"; - public string blockCommentEnd = @"*/"; - - public override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f); - - public override IEnumerable StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] }; - public override IEnumerable EndChars => new char[] { blockCommentEnd[0] }; - - public override bool IsImplicitMatch(CSLexer lexer) => IsMatch(lexer, lineCommentStart) || IsMatch(lexer, blockCommentStart); - - private bool IsMatch(CSLexer lexer, string commentType) - { - if (!string.IsNullOrEmpty(commentType)) - { - lexer.Rollback(); - - bool match = true; - for (int i = 0; i < commentType.Length; i++) - { - if (commentType[i] != lexer.ReadNext()) - { - match = false; - break; - } - } - - if (match) - { - // Read until end of line or file - while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) { } - - return true; - } - } - return false; - } - - private bool IsEndLineOrEndFile(CSLexer lexer, char character) => lexer.EndOfStream || character == '\n' || character == '\r'; - } -} diff --git a/src/UI/CSConsole/Lexer/KeywordLexer.cs b/src/UI/CSConsole/Lexer/KeywordLexer.cs new file mode 100644 index 0000000..c0d7a93 --- /dev/null +++ b/src/UI/CSConsole/Lexer/KeywordLexer.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace UnityExplorer.UI.CSharpConsole.Lexers +{ + public class KeywordLexer : Lexer + { + private readonly string[] Keywords = new[] { "add", "as", "ascending", "await", "bool", "break", "by", "byte", +"case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic", +"else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group", "if", "in", +"int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out", "ref", +"remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string", "switch", "throw", "true", +"try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield", "abstract", "async", "base", +"class", "delegate", "enum", "explicit", "extern", "fixed", "get", "implicit", "interface", "internal", +"namespace", "operator", "override", "params", "private", "protected", "public", "using", "partial", "readonly", +"sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" }; + + private readonly Dictionary> keywordsByLength = new Dictionary>(); + + public KeywordLexer() + { + foreach (var kw in Keywords) + { + if (!keywordsByLength.ContainsKey(kw.Length)) + keywordsByLength.Add(kw.Length, new HashSet()); + + keywordsByLength[kw.Length].Add(kw); + } + } + + protected override Color HighlightColor => new Color(0.33f, 0.61f, 0.83f, 1.0f); + + public override bool TryMatchCurrent(LexerBuilder lexer) + { + if (!lexer.IsDelimiter(lexer.Previous, true)) + return false; + + int len = 0; + var sb = new StringBuilder(); + while (!lexer.EndOfInput) + { + sb.Append(lexer.Current); + len++; + var next = lexer.PeekNext(); + if (lexer.IsDelimiter(next, true)) + { + lexer.RollbackBy(1); + break; + } + } + if (keywordsByLength.TryGetValue(len, out var keywords) && keywords.Contains(sb.ToString())) + { + lexer.Commit(); + return true; + } + + return false; + } + } +} diff --git a/src/UI/CSConsole/Lexer/KeywordMatch.cs b/src/UI/CSConsole/Lexer/KeywordMatch.cs deleted file mode 100644 index 12f768d..0000000 --- a/src/UI/CSConsole/Lexer/KeywordMatch.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace UnityExplorer.UI.CSharpConsole.Lexer -{ - public class KeywordMatch : Matcher - { - public string[] Keywords = new[] { "add", "as", "ascending", "await", "bool", "break", "by", "byte", -"case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic", -"else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group", -"if", "in", "int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out", -"ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string", -"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield", -"abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get", -"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public", -"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" }; - - public override Color HighlightColor => highlightColor; - public Color highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f); - - private readonly HashSet shortlist = new HashSet(); - private readonly Stack removeList = new Stack(); - - public override bool IsImplicitMatch(CSLexer lexer) - { - if (!char.IsWhiteSpace(lexer.Previous) && - !lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End)) - { - return false; - } - - shortlist.Clear(); - - int currentIndex = 0; - char currentChar = lexer.ReadNext(); - - for (int i = 0; i < Keywords.Length; i++) - { - if (Keywords[i][0] == currentChar) - shortlist.Add(Keywords[i]); - } - - if (shortlist.Count == 0) - return false; - - do - { - if (lexer.EndOfStream) - { - RemoveLongStrings(currentIndex + 1); - break; - } - - currentChar = lexer.ReadNext(); - currentIndex++; - - if (char.IsWhiteSpace(currentChar) || - lexer.IsSpecialSymbol(currentChar, DelimiterType.Start)) - { - RemoveLongStrings(currentIndex); - lexer.Rollback(1); - break; - } - - foreach (string keyword in shortlist) - { - if (currentIndex >= keyword.Length || keyword[currentIndex] != currentChar) - removeList.Push(keyword); - } - - while (removeList.Count > 0) - shortlist.Remove(removeList.Pop()); - } - while (shortlist.Count > 0); - - return shortlist.Count > 0; - } - - private void RemoveLongStrings(int length) - { - foreach (string keyword in shortlist) - { - if (keyword.Length > length) - removeList.Push(keyword); - } - - while (removeList.Count > 0) - shortlist.Remove(removeList.Pop()); - } - } -} diff --git a/src/UI/CSConsole/Lexer/Lexer.cs b/src/UI/CSConsole/Lexer/Lexer.cs new file mode 100644 index 0000000..c4f5c06 --- /dev/null +++ b/src/UI/CSConsole/Lexer/Lexer.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using UnityEngine; +using System.Linq; + +namespace UnityExplorer.UI.CSharpConsole.Lexers +{ + public abstract class Lexer + { + public virtual IEnumerable Delimiters => Enumerable.Empty(); + + protected abstract Color HighlightColor { get; } + + public string ColorTag => colorTag ?? (colorTag = ""); + private string colorTag; + + public abstract bool TryMatchCurrent(LexerBuilder lexer); + } +} diff --git a/src/UI/CSConsole/Lexer/Matcher.cs b/src/UI/CSConsole/Lexer/Matcher.cs deleted file mode 100644 index 4c73b79..0000000 --- a/src/UI/CSConsole/Lexer/Matcher.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using System.Linq; - -namespace UnityExplorer.UI.CSharpConsole.Lexer -{ - public abstract class Matcher - { - public abstract Color HighlightColor { get; } - - public string HexColorTag => htmlColor ?? (htmlColor = ""); - private string htmlColor; - - public virtual IEnumerable StartChars => Enumerable.Empty(); - public virtual IEnumerable EndChars => Enumerable.Empty(); - - public abstract bool IsImplicitMatch(CSLexer lexer); - - public bool IsMatch(CSLexer lexer) - { - if (IsImplicitMatch(lexer)) - { - lexer.Commit(); - return true; - } - - lexer.Rollback(); - return false; - } - } -} diff --git a/src/UI/CSConsole/Lexer/NumberLexer.cs b/src/UI/CSConsole/Lexer/NumberLexer.cs new file mode 100644 index 0000000..8cc9ccb --- /dev/null +++ b/src/UI/CSConsole/Lexer/NumberLexer.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace UnityExplorer.UI.CSharpConsole.Lexers +{ + public class NumberLexer : Lexer + { + protected override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f); + + private bool IsNumeric(char c) => char.IsNumber(c) || c == '.'; + + public override bool TryMatchCurrent(LexerBuilder lexer) + { + // previous character must be whitespace or delimiter + if (!lexer.IsDelimiter(lexer.Previous, true)) + return false; + + if (!IsNumeric(lexer.Current)) + return false; + + while (!lexer.EndOfInput) + { + lexer.Commit(); + if (!IsNumeric(lexer.PeekNext())) + break; + } + + return true; + } + } + +} diff --git a/src/UI/CSConsole/Lexer/NumberMatch.cs b/src/UI/CSConsole/Lexer/NumberMatch.cs deleted file mode 100644 index df194cf..0000000 --- a/src/UI/CSConsole/Lexer/NumberMatch.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UnityEngine; - -namespace UnityExplorer.UI.CSharpConsole.Lexer -{ - public class NumberMatch : Matcher - { - public override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f); - - public override bool IsImplicitMatch(CSLexer lexer) - { - if (!char.IsWhiteSpace(lexer.Previous) && - !lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End)) - { - return false; - } - - bool matchedNumber = false; - - while (!lexer.EndOfStream) - { - if (IsNumberOrDecimalPoint(lexer.ReadNext())) - { - matchedNumber = true; - lexer.Commit(); - } - else - { - lexer.Rollback(); - break; - } - } - - return matchedNumber; - } - - private bool IsNumberOrDecimalPoint(char character) => char.IsNumber(character) || character == '.'; - } - -} diff --git a/src/UI/CSConsole/Lexer/StringLexer.cs b/src/UI/CSConsole/Lexer/StringLexer.cs new file mode 100644 index 0000000..079a981 --- /dev/null +++ b/src/UI/CSConsole/Lexer/StringLexer.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace UnityExplorer.UI.CSharpConsole.Lexers +{ + public class StringLexer : Lexer + { + protected override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f); + + public override IEnumerable Delimiters => new[] { '"' }; + + public override bool TryMatchCurrent(LexerBuilder lexer) + { + if (lexer.Current != '"') + return false; + + if (lexer.Previous == '@') + { + // verbatim string, continue until un-escaped quote. + while (!lexer.EndOfInput) + { + lexer.Commit(); + if (lexer.PeekNext() == '"') + { + lexer.Commit(); + // possibly the end, check for escaped quotes. + // commit the character and flip the escape bool for each quote. + bool escaped = false; + while (lexer.PeekNext() == '"') + { + lexer.Commit(); + escaped = !escaped; + } + // if the last quote wasnt escaped, that was the end of the string. + if (!escaped) + break; + } + } + } + else + { + // normal string + // continue until a quote which is not escaped, or end of input + + while (!lexer.EndOfInput) + { + lexer.Commit(); + if (lexer.PeekNext() == '"' && lexer.Previous != '\\') + { + lexer.Commit(); + break; + } + } + } + + return true; + } + } +} diff --git a/src/UI/CSConsole/Lexer/StringMatch.cs b/src/UI/CSConsole/Lexer/StringMatch.cs deleted file mode 100644 index 685fbb6..0000000 --- a/src/UI/CSConsole/Lexer/StringMatch.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace UnityExplorer.UI.CSharpConsole.Lexer -{ - public class StringMatch : Matcher - { - public override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f); - - public override IEnumerable StartChars => new[] { '"' }; - public override IEnumerable EndChars => new[] { '"' }; - - public override bool IsImplicitMatch(CSLexer lexer) - { - if (lexer.ReadNext() == '"') - { - while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) { } - - return true; - } - return false; - } - - private bool IsClosingQuoteOrEndFile(CSLexer lexer, char character) => lexer.EndOfStream || character == '"'; - } -} diff --git a/src/UI/CSConsole/Lexer/SymbolLexer.cs b/src/UI/CSConsole/Lexer/SymbolLexer.cs new file mode 100644 index 0000000..af0a278 --- /dev/null +++ b/src/UI/CSConsole/Lexer/SymbolLexer.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace UnityExplorer.UI.CSharpConsole.Lexers +{ + public class SymbolLexer : Lexer + { + protected override Color HighlightColor => Color.white; + + // all symbols are delimiters + public override IEnumerable Delimiters => uniqueSymbols; + + // all symbol combinations are made of valid individual symbols. + private readonly HashSet uniqueSymbols = new HashSet + { + '[', ']', '{', '}', '(', ')', ',', '.', ';', ':', + '+', '-', '*', '/', '%', '&', '|', '^', '~', '=', + '<', '>', '?', '!', '@' + }; + +// // actual valid symbol combinations +// private readonly HashSet actualSymbols = new HashSet +// { +//"[", "]", "(", ")", "{", "}", ".", ",", ";", ":", "+", "-", "*", "/", "%", "&", "|", "^", "~", "=", +//"<", ">", "++", "--", "&&", "||", "<<", ">>", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", +//"&=", "|=", "^=", "<<=", ">>=", "->", "!", "?", "??", "@", "=>", +// }; + + public override bool TryMatchCurrent(LexerBuilder lexer) + { + // previous character must be delimiter, whitespace, or alphanumeric. + if (!lexer.IsDelimiter(lexer.Previous, true, true)) + return false; + + if (uniqueSymbols.Contains(lexer.Current)) + { + do + { + lexer.Commit(); + lexer.PeekNext(); + } + while (uniqueSymbols.Contains(lexer.Current)); + + return true; + } + + return false; + } + } +} diff --git a/src/UI/CSConsole/Lexer/SymbolMatch.cs b/src/UI/CSConsole/Lexer/SymbolMatch.cs deleted file mode 100644 index bc0ae23..0000000 --- a/src/UI/CSConsole/Lexer/SymbolMatch.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -namespace UnityExplorer.UI.CSharpConsole.Lexer -{ - public class SymbolMatch : Matcher - { - public override Color HighlightColor => new Color(0.58f, 0.47f, 0.37f, 1.0f); - - private readonly string[] symbols = new[] - { - "[", "]", "(", ")", ".", "?", ":", "+", "-", "*", "/", "%", "&", "|", "^", "~", "=", "<", ">", - "++", "--", "&&", "||", "<<", ">>", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "&=", - "|=", "^=", "<<=", ">>=", "->", "??", "=>", - }; - - private static readonly List shortlist = new List(); - private static readonly Stack removeList = new Stack(); - - public override IEnumerable StartChars => symbols.Select(s => s[0]); - public override IEnumerable EndChars => symbols.Select(s => s[0]); - - public override bool IsImplicitMatch(CSLexer lexer) - { - if (lexer == null) - return false; - - if (!char.IsWhiteSpace(lexer.Previous) && - !char.IsLetter(lexer.Previous) && - !char.IsDigit(lexer.Previous) && - !lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End)) - { - return false; - } - - shortlist.Clear(); - - int currentIndex = 0; - char currentChar = lexer.ReadNext(); - - for (int i = symbols.Length - 1; i >= 0; i--) - { - if (symbols[i][0] == currentChar) - shortlist.Add(symbols[i]); - } - - if (shortlist.Count == 0) - return false; - - do - { - if (lexer.EndOfStream) - { - RemoveLongStrings(currentIndex + 1); - break; - } - - currentChar = lexer.ReadNext(); - currentIndex++; - - if (char.IsWhiteSpace(currentChar) || - char.IsLetter(currentChar) || - char.IsDigit(currentChar) || - lexer.IsSpecialSymbol(currentChar, DelimiterType.Start)) - { - RemoveLongStrings(currentIndex); - lexer.Rollback(1); - break; - } - - foreach (string symbol in shortlist) - { - if (currentIndex >= symbol.Length || symbol[currentIndex] != currentChar) - removeList.Push(symbol); - } - - while (removeList.Count > 0) - shortlist.Remove(removeList.Pop()); - } - while (shortlist.Count > 0); - - return shortlist.Count > 0; - } - - private void RemoveLongStrings(int length) - { - foreach (string keyword in shortlist) - { - if (keyword.Length > length) - removeList.Push(keyword); - } - - while (removeList.Count > 0) - shortlist.Remove(removeList.Pop()); - } - } -} diff --git a/src/UI/CSConsole/LexerBuilder.cs b/src/UI/CSConsole/LexerBuilder.cs new file mode 100644 index 0000000..484db3a --- /dev/null +++ b/src/UI/CSConsole/LexerBuilder.cs @@ -0,0 +1,347 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using UnityExplorer.UI.CSharpConsole.Lexers; + +namespace UnityExplorer.UI.CSharpConsole +{ + public struct MatchInfo + { + public int startIndex; + public int endIndex; + public string htmlColorTag; + } + + public class LexerBuilder + { + // Initialization and core + + public const char WHITESPACE = ' '; + public const char INDENT_OPEN = '{'; + public const char INDENT_CLOSE = '}'; + + private readonly Lexer[] lexers; + private readonly HashSet delimiters = new HashSet(); + + public LexerBuilder() + { + lexers = new Lexer[] + { + new CommentLexer(), + new SymbolLexer(), + new StringLexer(), + new NumberLexer(), + new KeywordLexer(), + }; + + foreach (var matcher in lexers) + { + foreach (char c in matcher.Delimiters) + { + if (!delimiters.Contains(c)) + delimiters.Add(c); + } + } + } + + public bool IsDelimiter(char character, bool orWhitespace = false, bool orLetterOrDigit = false) + { + return delimiters.Contains(character) + || (orWhitespace && char.IsWhiteSpace(character)) + || (orLetterOrDigit && char.IsLetterOrDigit(character)); + } + + // Lexer enumeration + + public string InputString { get; private set; } + public int Length => InputString.Length; + + public int LastCommittedIndex { get; private set; } + public int LookaheadIndex { get; private set; } + + public char Current => !EndOfInput ? InputString[LookaheadIndex] : WHITESPACE; + public char Previous => LookaheadIndex >= 1 ? InputString[LookaheadIndex - 1] : WHITESPACE; + + public bool EndOfInput => LookaheadIndex >= Length; + public bool EndOrNewLine => EndOfInput || Current == '\n' || Current == '\r'; + + public string SyntaxHighlight(string input) + { + var sb = new StringBuilder(); + int lastUnhighlighted = 0; + + // TODO auto indent as part of this parse + + foreach (var match in GetMatches(input)) + { + // append non-highlighted text between last match and this + for (int i = lastUnhighlighted; i < match.startIndex; i++) + sb.Append(input[i]); + + // append the highlighted match + sb.Append(match.htmlColorTag); + + for (int i = match.startIndex; i <= match.endIndex && i < input.Length; i++) + sb.Append(input[i]); + + sb.Append(SignatureHighlighter.CLOSE_COLOR); + + // update the last unhighlighted start index + lastUnhighlighted = match.endIndex + 1; + } + + return sb.ToString(); + } + + public IEnumerable GetMatches(string input) + { + if (string.IsNullOrEmpty(input)) + yield break; + + InputString = input; + LastCommittedIndex = -1; + Rollback(); + + while (!EndOfInput) + { + SkipWhitespace(); + bool anyMatch = false; + int startIndex = LastCommittedIndex + 1; + + foreach (var lexer in lexers) + { + if (lexer.TryMatchCurrent(this)) + { + anyMatch = true; + + yield return new MatchInfo + { + startIndex = startIndex, + endIndex = LastCommittedIndex, + htmlColorTag = lexer.ColorTag, + }; + break; + } + else + Rollback(); + } + + if (!anyMatch) + { + LookaheadIndex = LastCommittedIndex + 1; + Commit(); + } + } + } + + public char PeekNext(int amount = 1) + { + LookaheadIndex += amount; + return Current; + } + + public void Commit() + { + LastCommittedIndex = Math.Min(Length - 1, LookaheadIndex); + } + + public void Rollback() + { + LookaheadIndex = LastCommittedIndex + 1; + } + + public void RollbackBy(int amount) + { + LookaheadIndex = Math.Max(LastCommittedIndex + 1, LookaheadIndex - amount); + } + + private void SkipWhitespace() + { + // peek and commit as long as there is whitespace + while (!EndOfInput && char.IsWhiteSpace(Current)) + { + Commit(); + PeekNext(); + } + + // revert the last PeekNext which would have returned false + Rollback(); + } + + #region AUTO INDENT TODO + + // 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 + } +} diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs index 09c7d64..6988794 100644 --- a/src/UI/Panels/CSConsolePanel.cs +++ b/src/UI/Panels/CSConsolePanel.cs @@ -159,7 +159,7 @@ namespace UnityExplorer.UI.Panels InputText = InputField.InputField.textComponent; InputText.supportRichText = false; - InputText.color = new Color(1, 1, 1, 0.5f); + InputText.color = new Color(1, 1, 1, 0.65f); var mainTextObj = InputText.gameObject; var highlightTextObj = UIFactory.CreateUIObject("HighlightText", mainTextObj.gameObject); @@ -171,6 +171,7 @@ namespace UnityExplorer.UI.Panels highlightTextRect.offsetMax = new Vector2(14, 0); HighlightText = highlightTextObj.AddComponent(); + HighlightText.color = Color.clear; HighlightText.supportRichText = true; HighlightText.fontSize = fontSize; diff --git a/src/UI/UIManager.cs b/src/UI/UIManager.cs index 66815a7..09b4138 100644 --- a/src/UI/UIManager.cs +++ b/src/UI/UIManager.cs @@ -119,9 +119,7 @@ namespace UnityExplorer.UI UIPanel.UpdateFocus(); PanelDragger.UpdateInstances(); - UIBehaviourModel.UpdateInstances(); - AutoCompleter.Update(); } public static void TogglePanel(Panels panel) diff --git a/src/UnityExplorer.csproj b/src/UnityExplorer.csproj index 80171b3..e11898f 100644 --- a/src/UnityExplorer.csproj +++ b/src/UnityExplorer.csproj @@ -219,13 +219,13 @@ - - - - - - - + + + + + + +