From 6a3d01a4e2b4d2f2ed1890bd474dee14ab4ef416 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sun, 24 May 2020 18:19:48 +0800 Subject: [PATCH 01/15] Implemented for fractions, radicals and superscripts --- CSharpMath.Editor/Extensions/MathList.cs | 58 +++++++++++++++++++++--- CSharpMath.Editor/MathKeyboard.cs | 12 ++++- CSharpMath.Editor/MathListIndex.cs | 6 +++ CSharpMath/Atom/MathList.cs | 2 +- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/CSharpMath.Editor/Extensions/MathList.cs b/CSharpMath.Editor/Extensions/MathList.cs index 050f0870..de6cfdac 100644 --- a/CSharpMath.Editor/Extensions/MathList.cs +++ b/CSharpMath.Editor/Extensions/MathList.cs @@ -4,6 +4,8 @@ namespace CSharpMath.Editor { using Atom; using Atoms = Atom.Atoms; using Structures; + using System.Linq; + partial class Extensions { static void InsertAtAtomIndexAndAdvance(this MathList self, int atomIndex, MathAtom atom, ref MathListIndex advance, MathListSubIndexType advanceType) { if (atomIndex < 0 || atomIndex > self.Count) @@ -81,6 +83,46 @@ public static void InsertAndAdvance(this MathList self, ref MathListIndex index, } public static void RemoveAt(this MathList self, ref MathListIndex index) { + void RemoveAtInnerList(ref MathListIndex index, TAtom atom, int innerListIndex) where TAtom : MathAtom, IMathListContainer { + if (index.SubIndex is null) throw new InvalidCodePathException($"{nameof(index.SubIndex)} should exist"); + if (index.IsBeforeSubList) { + index = index.LevelDown() + ?? throw new InvalidCodePathException($"{nameof(index.SubIndex)} is not null but {nameof(index.LevelDown)} is null"); + self.RemoveAt(ref index); + MathListIndex tempIndex = index; + int i = 0; + foreach (var innerList in atom.InnerLists) + if (!(innerList.Count == 1 && innerList[0] is Atoms.Placeholder)) + if (i++ < innerListIndex) { + foreach (var inner in innerList) + self.InsertAndAdvance(ref index, inner, MathListSubIndexType.None); + tempIndex = index; + } + else + foreach (var inner in innerList) + self.InsertAndAdvance(ref tempIndex, inner, MathListSubIndexType.None); + if(index.SubIndexType != MathListSubIndexType.None && tempIndex.AtomIndex == 0 // We deleted an atom only consisting of placeholders + || atom.Superscript.Count > 0 || atom.Subscript.Count > 0) + self.InsertAndAdvance(ref tempIndex, LaTeXSettings.Placeholder, MathListSubIndexType.None); + if(atom.Superscript.Count > 0) self[tempIndex.AtomIndex - 1].Superscript.Append(atom.Superscript); + if(atom.Subscript.Count > 0) self[tempIndex.AtomIndex - 1].Subscript.Append(atom.Subscript); + } else atom.InnerLists.ElementAt(innerListIndex).RemoveAt(ref index.SubIndex); + } + void RemoveAtInnerScript(ref MathListIndex index, MathAtom atom, bool superscript) { + if (index.SubIndex is null) throw new InvalidCodePathException($"{nameof(index.SubIndex)} should exist"); + if (index.IsBeforeSubList) { + index = index.LevelDown() + ?? throw new InvalidCodePathException($"{nameof(index.SubIndex)} is not null but {nameof(index.LevelDown)} is null"); + var tempIndex = index; + if (superscript) { + if (atom.Superscript.Count == 1 && atom.Superscript[0] is Atoms.Placeholder) + foreach (var inner in atom.Superscript) + self.InsertAndAdvance(ref tempIndex, inner, MathListSubIndexType.None); + atom.Superscript.Clear(); + } + } else (superscript ? atom.Superscript : atom.Subscript).RemoveAt(ref index.SubIndex); + } + index ??= MathListIndex.Level0Index(0); if (index.AtomIndex > self.Atoms.Count) throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); @@ -132,28 +174,30 @@ public static void RemoveAt(this MathList self, ref MathListIndex index) { if (!(self.Atoms[index.AtomIndex] is Atoms.Radical radical)) throw new SubIndexTypeMismatchException(typeof(Atoms.Radical), index); if (index.SubIndexType == MathListSubIndexType.Degree) - radical.Degree.RemoveAt(ref index.SubIndex); - else radical.Radicand.RemoveAt(ref index.SubIndex); + RemoveAtInnerList(ref index, radical, 0); + else + RemoveAtInnerList(ref index, radical, 1); break; case MathListSubIndexType.Numerator: case MathListSubIndexType.Denominator: if (!(self.Atoms[index.AtomIndex] is Atoms.Fraction frac)) throw new SubIndexTypeMismatchException(typeof(Atoms.Fraction), index); if (index.SubIndexType == MathListSubIndexType.Numerator) - frac.Numerator.RemoveAt(ref index.SubIndex); - else frac.Denominator.RemoveAt(ref index.SubIndex); + RemoveAtInnerList(ref index, frac, 0); + else + RemoveAtInnerList(ref index, frac, 1); break; case MathListSubIndexType.Subscript: var current = self.Atoms[index.AtomIndex]; if (current.Subscript.IsEmpty()) throw new SubIndexTypeMismatchException(index); - current.Subscript.RemoveAt(ref index.SubIndex); + RemoveAtInnerScript(ref index, current, false); break; case MathListSubIndexType.Superscript: current = self.Atoms[index.AtomIndex]; if (current.Superscript.IsEmpty()) throw new SubIndexTypeMismatchException(index); - current.Superscript.RemoveAt(ref index.SubIndex); + RemoveAtInnerScript(ref index, current, true); break; case MathListSubIndexType.Inner: if (!(self.Atoms[index.AtomIndex] is Atoms.Inner inner)) @@ -167,7 +211,7 @@ public static void RemoveAt(this MathList self, ref MathListIndex index) { // We have deleted to the beginning of the line and it is not the outermost line if (self.AtomAt(index) is null) { self.InsertAndAdvance(ref index, LaTeXSettings.Placeholder, MathListSubIndexType.None); - index = index.Previous ?? throw new InvalidCodePathException("Cannot go back after insertion?"); ; + index = index.Previous ?? throw new InvalidCodePathException("Cannot go back after insertion?"); } } } diff --git a/CSharpMath.Editor/MathKeyboard.cs b/CSharpMath.Editor/MathKeyboard.cs index efb3a007..9ca7af13 100644 --- a/CSharpMath.Editor/MathKeyboard.cs +++ b/CSharpMath.Editor/MathKeyboard.cs @@ -87,7 +87,7 @@ public MathListIndex InsertionIndex { public LineStyle LineStyle { get; set; } public Structures.Color SelectColor { get; set; } public virtual RectangleF Measure => Display?.DisplayBounds() ?? RectangleF.Empty; - public bool HasText => MathList?.Atoms?.Count > 0; + public bool HasText => MathList.Atoms.Count > 0; public void RecreateDisplayFromMathList() { var position = Display?.Position ?? default; Display = Typesetter.CreateLine(MathList, Font, Context, LineStyle); @@ -389,9 +389,17 @@ void MoveCursorRight() { void DeleteBackwards() { // delete the last atom from the list - if (HasText && _insertionIndex.Previous is MathListIndex previous) { + if (HasText && _insertionIndex.PreviousOrBeforeWholeList is MathListIndex previous) { _insertionIndex = previous; MathList.RemoveAt(ref _insertionIndex); + } else { + var index = _insertionIndex.LevelDown(); + if (index != null) { + switch (_insertionIndex.FinalSubIndexType) { + default: + break; + } + } } } diff --git a/CSharpMath.Editor/MathListIndex.cs b/CSharpMath.Editor/MathListIndex.cs index cb880cd3..17926a44 100644 --- a/CSharpMath.Editor/MathListIndex.cs +++ b/CSharpMath.Editor/MathListIndex.cs @@ -80,6 +80,12 @@ public MathListIndex LevelUpWithSubIndex(MathListSubIndexType type, MathListInde MathListSubIndexType.None => AtomIndex > 0 ? Level0Index(AtomIndex - 1) : null, _ => SubIndex?.Previous is MathListIndex prevSubIndex ? IndexAtLocation(AtomIndex, SubIndexType, prevSubIndex) : null, }; + internal bool IsBeforeSubList => SubIndex is { AtomIndex: -1, SubIndexType: MathListSubIndexType.None }; + internal MathListIndex? PreviousOrBeforeWholeList => SubIndexType switch + { + MathListSubIndexType.None => AtomIndex > -1 ? Level0Index(AtomIndex - 1) : null, + _ => SubIndex?.PreviousOrBeforeWholeList is MathListIndex prevSubIndex ? IndexAtLocation(AtomIndex, SubIndexType, prevSubIndex) : null, + }; ///Returns the next index. public MathListIndex Next => SubIndexType switch diff --git a/CSharpMath/Atom/MathList.cs b/CSharpMath/Atom/MathList.cs index 7cfeeba2..d2a2f4b0 100644 --- a/CSharpMath/Atom/MathList.cs +++ b/CSharpMath/Atom/MathList.cs @@ -4,7 +4,7 @@ namespace CSharpMath.Atom { #pragma warning disable CA1710 // Identifiers should have correct suffix - // WTF CA1710, you want types inheriting IList to have the Collection suffix? + // WTF CA1710, you want types implementing IList to have the Collection suffix? class DisabledMathList : MathList { internal DisabledMathList() { } public override void Add(MathAtom item) => throw new InvalidOperationException("Scripts are not allowed!"); From 9e8e43521b7470ecc083b480d4684c059f716dd8 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sun, 24 May 2020 23:18:41 +0800 Subject: [PATCH 02/15] Added tests --- CSharpMath.Editor.Tests/KeyPressTests.cs | 61 ++++++++++++++++++++++-- CSharpMath.Editor/Extensions/MathList.cs | 26 ++++++---- CSharpMath.Editor/MathKeyboard.cs | 2 +- CSharpMath.Editor/MathListIndex.cs | 2 + 4 files changed, 76 insertions(+), 15 deletions(-) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index e5d84b13..2099100e 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -227,11 +227,27 @@ public void Return(params K[] inputs) => [ Theory, T(@"", K.Backspace, K.Backspace, K.Backspace, K.Backspace, K.Backspace), + T(@"2", K.Backspace, K.Backspace, K.D1, K.Backspace, K.D2), T(@"1", K.D1, K.D2, K.Backspace), T(@"x^2", K.SmallX, K.Power, K.D2, K.D1, K.Backspace), - T(@"y_{3_4}", K.SmallY, K.Subscript, K.D3, K.Subscript, K.Backspace, K.Backspace, K.D4, K.D5, K.Backspace), - T(@"5^■", K.D5, K.Power, K.Iota, K.Kappa, K.SmallEta, K.Backspace, K.Backspace, K.Backspace, K.Backspace), - T(@"\frac{■}{\square }", K.Fraction, K.Backspace), + T(@"5", K.D5, K.Power, K.Iota, K.Kappa, K.SmallEta, K.Backspace, K.Backspace, K.Backspace, K.Backspace), + T(@"", K.Fraction, K.Backspace), + T(@"", K.Power, K.Backspace), + T(@"", K.Subscript, K.Backspace), + T(@"", K.SquareRoot, K.Backspace), + T(@"3", K.CubeRoot, K.Backspace), + T(@"", K.NthRoot, K.Backspace), + T(@"a", K.SmallA, K.Fraction, K.Backspace), + T(@"a", K.SmallA, K.SquareRoot, K.Backspace), + T(@"a3", K.SmallA, K.CubeRoot, K.Backspace), + T(@"a", K.SmallA, K.NthRoot, K.Backspace), + T(@"a", K.SmallA, K.Power, K.Backspace), + T(@"a", K.SmallA, K.Subscript, K.Backspace), + T(@"\square ^■", K.Power, K.Subscript, K.Backspace), + T(@"\square ^■", K.Power, K.Power, K.Backspace), + T(@"\square _■", K.Subscript, K.Power, K.Backspace), + T(@"\square _■", K.Subscript, K.Subscript, K.Backspace), + T(@"y_4", K.SmallY, K.Subscript, K.D3, K.Subscript, K.Backspace, K.Backspace, K.D4, K.D5, K.Backspace), T(@"", K.VerticalBar, K.VerticalBar, K.Backspace, K.Backspace, K.Backspace) ] public void Backspace(string latex, params K[] inputs) => Test(latex, inputs); @@ -239,6 +255,43 @@ public void Return(params K[] inputs) => [ Theory, T(@"", K.Left, K.Left, K.Backspace, K.Backspace, K.Right, K.Right, K.Backspace, K.Backspace, K.Left), + T(@"\frac{\square }{\square }", K.Fraction, K.Left, K.Backspace), + T(@"\sqrt{\square }", K.SquareRoot, K.Left, K.Backspace), + T(@"\sqrt[\square ]{\square }", K.NthRoot, K.Left, K.Backspace), + T(@"", K.Power, K.Left, K.Backspace), + T(@"", K.Subscript, K.Left, K.Backspace), + T(@".bcd", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"", K.Fraction, K.Right, K.Backspace), + T(@"", K.SquareRoot, K.Right, K.Backspace), + T(@"", K.NthRoot, K.Right, K.Backspace), + T(@"", K.Power, K.Right, K.Backspace), + T(@"", K.Subscript, K.Right, K.Backspace), + T(@"bcd.", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"bcd.", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"", K.Fraction, K.Right, K.Right, K.Backspace), + T(@"", K.SquareRoot, K.Right, K.Right, K.Backspace), + T(@"", K.NthRoot, K.Right, K.Right, K.Backspace), + T(@"", K.Power, K.Right, K.Right, K.Backspace), + T(@"", K.Subscript, K.Right, K.Right, K.Backspace), + T(@"bcd.", K.Fraction, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.", K.NthRoot, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.Fraction, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.NthRoot, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.efg", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.efg", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.efg", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.efg", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"\frac{\square }{3}", K.Slash, K.D3, K.Left, K.Left, K.Backspace, K.Left), T(@"1_3", K.D1, K.D2, K.Subscript, K.D3, K.Left, K.Left, K.Backspace), T(@"1_3^2", K.D1, K.D4, K.Subscript, K.D3, K.Left, K.Left, K.Power, K.D2, K.Left, K.Left, K.Left, K.Left, K.Backspace), @@ -247,7 +300,7 @@ public void Return(params K[] inputs) => T(@"\sqrt[■]{\square }", K.NthRoot, K.SmallA, K.Backspace), T(@"\sqrt{■}", K.SquareRoot, K.SmallA, K.Backspace), T(@"\frac{1}{■}", K.Slash, K.D6, K.Backspace), - T(@"■_5", K.Subscript, K.D5, K.Left, K.Left, K.Backspace, K.X, K.Left, K.Left, K.Left, K.Backspace), + T(@"", K.Subscript, K.D5, K.Left, K.Left, K.Backspace), T(@"7+1^X", K.D7, K.Plus, K.D1, K.D2, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7.^X", K.D7, K.Decimal, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7+■^X", K.D7, K.Plus, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), diff --git a/CSharpMath.Editor/Extensions/MathList.cs b/CSharpMath.Editor/Extensions/MathList.cs index de6cfdac..fa782f1b 100644 --- a/CSharpMath.Editor/Extensions/MathList.cs +++ b/CSharpMath.Editor/Extensions/MathList.cs @@ -110,25 +110,31 @@ void RemoveAtInnerList(ref MathListIndex index, TAtom atom, int innerList } void RemoveAtInnerScript(ref MathListIndex index, MathAtom atom, bool superscript) { if (index.SubIndex is null) throw new InvalidCodePathException($"{nameof(index.SubIndex)} should exist"); + var script = superscript ? atom.Superscript : atom.Subscript; if (index.IsBeforeSubList) { index = index.LevelDown() ?? throw new InvalidCodePathException($"{nameof(index.SubIndex)} is not null but {nameof(index.LevelDown)} is null"); + if (atom is Atoms.Placeholder && (superscript ? atom.Subscript : atom.Superscript).Count == 0) + self.RemoveAt(index.AtomIndex); + else index = index.Next; var tempIndex = index; - if (superscript) { - if (atom.Superscript.Count == 1 && atom.Superscript[0] is Atoms.Placeholder) - foreach (var inner in atom.Superscript) - self.InsertAndAdvance(ref tempIndex, inner, MathListSubIndexType.None); - atom.Superscript.Clear(); - } - } else (superscript ? atom.Superscript : atom.Subscript).RemoveAt(ref index.SubIndex); + if (!(script.Count == 1 && script[0] is Atoms.Placeholder)) + foreach (var inner in script) + self.InsertAndAdvance(ref tempIndex, inner, MathListSubIndexType.None); + script.Clear(); + } else script.RemoveAt(ref index.SubIndex); } - index ??= MathListIndex.Level0Index(0); if (index.AtomIndex > self.Atoms.Count) throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); switch (index.SubIndexType) { case MathListSubIndexType.None: - self.RemoveAt(index.AtomIndex); + if (index.AtomIndex == -1) { + index = index.Next; + if (self.Atoms[index.AtomIndex] is Atoms.Placeholder) + self.RemoveAt(index.AtomIndex); + } else + self.RemoveAt(index.AtomIndex); break; case var _ when index.SubIndex is null: throw new InvalidCodePathException("index.SubIndex is null despite non-None subindex type"); @@ -202,7 +208,7 @@ void RemoveAtInnerScript(ref MathListIndex index, MathAtom atom, bool superscrip case MathListSubIndexType.Inner: if (!(self.Atoms[index.AtomIndex] is Atoms.Inner inner)) throw new SubIndexTypeMismatchException(typeof(Atoms.Inner), index); - inner.InnerList.RemoveAt(ref index.SubIndex); + RemoveAtInnerList(ref index, inner, 0); break; default: throw new SubIndexTypeMismatchException(index); diff --git a/CSharpMath.Editor/MathKeyboard.cs b/CSharpMath.Editor/MathKeyboard.cs index 9ca7af13..4c473033 100644 --- a/CSharpMath.Editor/MathKeyboard.cs +++ b/CSharpMath.Editor/MathKeyboard.cs @@ -499,7 +499,7 @@ void InsertSymbolName(string name, bool subscript = false, bool superscript = fa break; case MathKeyboardInput.BaseEPower: InsertAtom(LaTeXSettings.ForAscii((sbyte)'e') - ?? throw new InvalidCodePathException("LaTeXDefaults.ForAscii((byte)'e') is null")); + ?? throw new InvalidCodePathException($"{nameof(LaTeXSettings.ForAscii)}((sbyte)'e') is null")); HandleScriptButton(true); break; case MathKeyboardInput.Logarithm: diff --git a/CSharpMath.Editor/MathListIndex.cs b/CSharpMath.Editor/MathListIndex.cs index 17926a44..4c6977b5 100644 --- a/CSharpMath.Editor/MathListIndex.cs +++ b/CSharpMath.Editor/MathListIndex.cs @@ -80,7 +80,9 @@ public MathListIndex LevelUpWithSubIndex(MathListSubIndexType type, MathListInde MathListSubIndexType.None => AtomIndex > 0 ? Level0Index(AtomIndex - 1) : null, _ => SubIndex?.Previous is MathListIndex prevSubIndex ? IndexAtLocation(AtomIndex, SubIndexType, prevSubIndex) : null, }; + /// Should be used inside only! internal bool IsBeforeSubList => SubIndex is { AtomIndex: -1, SubIndexType: MathListSubIndexType.None }; + /// Valid for only! internal MathListIndex? PreviousOrBeforeWholeList => SubIndexType switch { MathListSubIndexType.None => AtomIndex > -1 ? Level0Index(AtomIndex - 1) : null, From 98aa35dfcb308c9e3d997e23fc3a41d295989b9e Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sun, 24 May 2020 23:29:53 +0800 Subject: [PATCH 03/15] More tests --- CSharpMath.Editor.Tests/KeyPressTests.cs | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index 2099100e..1ba83928 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -255,11 +255,21 @@ public void Return(params K[] inputs) => [ Theory, T(@"", K.Left, K.Left, K.Backspace, K.Backspace, K.Right, K.Right, K.Backspace, K.Backspace, K.Left), - T(@"\frac{\square }{\square }", K.Fraction, K.Left, K.Backspace), - T(@"\sqrt{\square }", K.SquareRoot, K.Left, K.Backspace), - T(@"\sqrt[\square ]{\square }", K.NthRoot, K.Left, K.Backspace), - T(@"", K.Power, K.Left, K.Backspace), - T(@"", K.Subscript, K.Left, K.Backspace), + T(@".\frac{\square }{\square }", K.Fraction, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt{\square }", K.SquareRoot, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt[\square ]{\square }", K.NthRoot, K.Left, K.Backspace, K.Decimal), + T(@".", K.Power, K.Left, K.Backspace, K.Decimal), + T(@".", K.Subscript, K.Left, K.Backspace, K.Decimal), + T(@".", K.Fraction, K.Right, K.Backspace, K.Decimal), + T(@".", K.SquareRoot, K.Right, K.Backspace, K.Decimal), + T(@".", K.NthRoot, K.Right, K.Backspace, K.Decimal), + T(@".", K.Power, K.Right, K.Backspace, K.Decimal), + T(@".", K.Subscript, K.Right, K.Backspace, K.Decimal), + T(@".", K.Fraction, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.SquareRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.NthRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.Power, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.Subscript, K.Right, K.Right, K.Backspace, K.Decimal), T(@".bcd", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@".bcd", K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@".bcd", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), @@ -270,20 +280,10 @@ public void Return(params K[] inputs) => T(@"a.bcd", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), - T(@"", K.Fraction, K.Right, K.Backspace), - T(@"", K.SquareRoot, K.Right, K.Backspace), - T(@"", K.NthRoot, K.Right, K.Backspace), - T(@"", K.Power, K.Right, K.Backspace), - T(@"", K.Subscript, K.Right, K.Backspace), T(@"bcd.", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), T(@"bcd.", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), T(@"abcd.", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), T(@"abcd.", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), - T(@"", K.Fraction, K.Right, K.Right, K.Backspace), - T(@"", K.SquareRoot, K.Right, K.Right, K.Backspace), - T(@"", K.NthRoot, K.Right, K.Right, K.Backspace), - T(@"", K.Power, K.Right, K.Right, K.Backspace), - T(@"", K.Subscript, K.Right, K.Right, K.Backspace), T(@"bcd.", K.Fraction, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"bcd.", K.NthRoot, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"abcd.", K.SmallA, K.Fraction, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), From eccb0a83c41ad2386fe5de3a3dfcca7f836c49ea Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sun, 24 May 2020 23:42:24 +0800 Subject: [PATCH 04/15] Tests for Inner --- CSharpMath.Editor.Tests/KeyPressTests.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index 1ba83928..e1d137b9 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -258,26 +258,49 @@ public void Return(params K[] inputs) => T(@".\frac{\square }{\square }", K.Fraction, K.Left, K.Backspace, K.Decimal), T(@".\sqrt{\square }", K.SquareRoot, K.Left, K.Backspace, K.Decimal), T(@".\sqrt[\square ]{\square }", K.NthRoot, K.Left, K.Backspace, K.Decimal), + T(@".\left( \square \right) ", K.BothRoundBrackets, K.Left, K.Backspace, K.Decimal), T(@".", K.Power, K.Left, K.Backspace, K.Decimal), T(@".", K.Subscript, K.Left, K.Backspace, K.Decimal), + T(@".\frac{\square }{\square }", K.SmallA, K.Fraction, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt{\square }", K.SmallA, K.SquareRoot, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt[\square ]{\square }", K.SmallA, K.NthRoot, K.Left, K.Backspace, K.Decimal), + T(@".\left( \square \right) ", K.SmallA, K.BothRoundBrackets, K.Left, K.Backspace, K.Decimal), + T(@".^{\square }", K.SmallA, K.Power, K.Left, K.Backspace, K.Decimal), + T(@"._{\square }", K.SmallA, K.Subscript, K.Left, K.Backspace, K.Decimal), T(@".", K.Fraction, K.Right, K.Backspace, K.Decimal), T(@".", K.SquareRoot, K.Right, K.Backspace, K.Decimal), T(@".", K.NthRoot, K.Right, K.Backspace, K.Decimal), + T(@".", K.BothRoundBrackets, K.Right, K.Backspace, K.Decimal), T(@".", K.Power, K.Right, K.Backspace, K.Decimal), T(@".", K.Subscript, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.Fraction, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.SquareRoot, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.NthRoot, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.BothRoundBrackets, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Power, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Subscript, K.Right, K.Backspace, K.Decimal), T(@".", K.Fraction, K.Right, K.Right, K.Backspace, K.Decimal), T(@".", K.SquareRoot, K.Right, K.Right, K.Backspace, K.Decimal), T(@".", K.NthRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.BothRoundBrackets, K.Right, K.Right, K.Backspace, K.Decimal), T(@".", K.Power, K.Right, K.Right, K.Backspace, K.Decimal), T(@".", K.Subscript, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.Fraction, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.SquareRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.NthRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.BothRoundBrackets, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Power, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Subscript, K.Right, K.Right, K.Backspace, K.Decimal), T(@".bcd", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@".bcd", K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@".bcd", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.BothRoundBrackets, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@".bcd", K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@".bcd", K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.BothRoundBrackets, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"a.bcd", K.SmallA, K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"bcd.", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), From ab4a4a7c48befbadc2bf9e65e3b3e2fdcc7355f1 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Mon, 25 May 2020 00:03:32 +0800 Subject: [PATCH 05/15] Delete trash --- CSharpMath.Editor/MathKeyboard.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CSharpMath.Editor/MathKeyboard.cs b/CSharpMath.Editor/MathKeyboard.cs index 4c473033..3c6fd48f 100644 --- a/CSharpMath.Editor/MathKeyboard.cs +++ b/CSharpMath.Editor/MathKeyboard.cs @@ -392,14 +392,6 @@ void DeleteBackwards() { if (HasText && _insertionIndex.PreviousOrBeforeWholeList is MathListIndex previous) { _insertionIndex = previous; MathList.RemoveAt(ref _insertionIndex); - } else { - var index = _insertionIndex.LevelDown(); - if (index != null) { - switch (_insertionIndex.FinalSubIndexType) { - default: - break; - } - } } } From 638bd1ef1e3beea2980968a78f23942ddbcb2b54 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Mon, 25 May 2020 00:14:15 +0800 Subject: [PATCH 06/15] Tests for delete at beginnning of script when both scripts are present --- CSharpMath.Editor.Tests/KeyPressTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index e1d137b9..a5fda0d2 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -315,6 +315,22 @@ public void Return(params K[] inputs) => T(@"bcd.efg", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"abcd.efg", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"abcd.efg", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"\square ^{\square }", K.Power, K.Right, K.Subscript, K.Backspace), + T(@"\square _{\square }", K.Subscript, K.Right, K.Power, K.Backspace), + T(@"X^{\square }", K.X, K.Power, K.Right, K.Subscript, K.Backspace), + T(@"X_{\square }", K.X, K.Subscript, K.Right, K.Power, K.Backspace), + T(@"\square ^Z", K.Power, K.Z, K.Right, K.Subscript, K.Backspace), + T(@"\square _{\square }Z", K.Subscript, K.Right, K.Power, K.Z, K.Left, K.Backspace), + T(@"X^Z", K.X, K.Power, K.Z, K.Right, K.Subscript, K.Backspace), + T(@"X_{\square }Z", K.X, K.Subscript, K.Right, K.Power, K.Z, K.Left, K.Backspace), + T(@"\square ^{\square }Y", K.Power, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"\square _Y", K.Subscript, K.Y, K.Right, K.Power, K.Backspace), + T(@"X^{\square }Y", K.X, K.Power, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"X_Y", K.X, K.Subscript, K.Y, K.Right, K.Power, K.Backspace), + T(@"\square ^ZY", K.Power, K.Z, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"\square _YZ", K.Subscript, K.Y, K.Right, K.Power, K.Z, K.Left, K.Backspace), + T(@"X^ZY", K.X, K.Power, K.Z, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"X_YZ", K.X, K.Subscript, K.Y, K.Right, K.Power, K.Z, K.Left, K.Backspace), T(@"\frac{\square }{3}", K.Slash, K.D3, K.Left, K.Left, K.Backspace, K.Left), T(@"1_3", K.D1, K.D2, K.Subscript, K.D3, K.Left, K.Left, K.Backspace), T(@"1_3^2", K.D1, K.D4, K.Subscript, K.D3, K.Left, K.Left, K.Power, K.D2, K.Left, K.Left, K.Left, K.Left, K.Backspace), From f6db396b6c09b6f75d04cbf1bd488e24a3df9829 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Mon, 25 May 2020 00:22:59 +0800 Subject: [PATCH 07/15] Also add power counterpart for this test --- CSharpMath.Editor.Tests/KeyPressTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index a5fda0d2..a393ad14 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -339,6 +339,7 @@ public void Return(params K[] inputs) => T(@"\sqrt[■]{\square }", K.NthRoot, K.SmallA, K.Backspace), T(@"\sqrt{■}", K.SquareRoot, K.SmallA, K.Backspace), T(@"\frac{1}{■}", K.Slash, K.D6, K.Backspace), + T(@"", K.Power, K.D5, K.Left, K.Left, K.Backspace), T(@"", K.Subscript, K.D5, K.Left, K.Left, K.Backspace), T(@"7+1^X", K.D7, K.Plus, K.D1, K.D2, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7.^X", K.D7, K.Decimal, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), From 8e2d2cadf0e2a108c19dd3db93326508a1da8180 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Mon, 25 May 2020 00:24:10 +0800 Subject: [PATCH 08/15] And placeholder counterparts --- CSharpMath.Editor.Tests/KeyPressTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index a393ad14..63fbad24 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -339,6 +339,8 @@ public void Return(params K[] inputs) => T(@"\sqrt[■]{\square }", K.NthRoot, K.SmallA, K.Backspace), T(@"\sqrt{■}", K.SquareRoot, K.SmallA, K.Backspace), T(@"\frac{1}{■}", K.Slash, K.D6, K.Backspace), + T(@"", K.Power, K.Left, K.Backspace), + T(@"", K.Subscript, K.Left, K.Backspace), T(@"", K.Power, K.D5, K.Left, K.Left, K.Backspace), T(@"", K.Subscript, K.D5, K.Left, K.Left, K.Backspace), T(@"7+1^X", K.D7, K.Plus, K.D1, K.D2, K.Power, K.X, K.Left, K.Left, K.Backspace), From fc58258c1344b002e96a2ed5de89a112870232d7 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Mon, 25 May 2020 14:12:35 +0800 Subject: [PATCH 09/15] Deleting placeholders at the start should not eliminate scripts --- CSharpMath.Editor.Tests/KeyPressTests.cs | 10 ++++++---- CSharpMath.Editor/Extensions/MathList.cs | 10 +++++++++- CSharpMath.Evaluation/Interpret.cs | 2 +- .../Resources/Resource.designer.cs | 1 - 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index 63fbad24..807530db 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -255,6 +255,8 @@ public void Return(params K[] inputs) => [ Theory, T(@"", K.Left, K.Left, K.Backspace, K.Backspace, K.Right, K.Right, K.Backspace, K.Backspace, K.Left), + T(@"", K.Power, K.Left, K.Backspace), + T(@"", K.Subscript, K.Left, K.Backspace), T(@".\frac{\square }{\square }", K.Fraction, K.Left, K.Backspace, K.Decimal), T(@".\sqrt{\square }", K.SquareRoot, K.Left, K.Backspace, K.Decimal), T(@".\sqrt[\square ]{\square }", K.NthRoot, K.Left, K.Backspace, K.Decimal), @@ -315,6 +317,10 @@ public void Return(params K[] inputs) => T(@"bcd.efg", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"abcd.efg", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"abcd.efg", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".456", K.Subscript, K.D4, K.D5, K.D6, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".789", K.Power, K.D7, K.D8, K.D9, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".456789", K.Subscript, K.D4, K.D5, K.D6, K.Right, K.Power, K.D7, K.D8, K.D9, K.Left, K.Left, K.Left, K.Left, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".456789", K.Power, K.D7, K.D8, K.D9, K.Right, K.Subscript, K.D4, K.D5, K.D6, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), T(@"\square ^{\square }", K.Power, K.Right, K.Subscript, K.Backspace), T(@"\square _{\square }", K.Subscript, K.Right, K.Power, K.Backspace), T(@"X^{\square }", K.X, K.Power, K.Right, K.Subscript, K.Backspace), @@ -339,10 +345,6 @@ public void Return(params K[] inputs) => T(@"\sqrt[■]{\square }", K.NthRoot, K.SmallA, K.Backspace), T(@"\sqrt{■}", K.SquareRoot, K.SmallA, K.Backspace), T(@"\frac{1}{■}", K.Slash, K.D6, K.Backspace), - T(@"", K.Power, K.Left, K.Backspace), - T(@"", K.Subscript, K.Left, K.Backspace), - T(@"", K.Power, K.D5, K.Left, K.Left, K.Backspace), - T(@"", K.Subscript, K.D5, K.Left, K.Left, K.Backspace), T(@"7+1^X", K.D7, K.Plus, K.D1, K.D2, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7.^X", K.D7, K.Decimal, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7+■^X", K.D7, K.Plus, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), diff --git a/CSharpMath.Editor/Extensions/MathList.cs b/CSharpMath.Editor/Extensions/MathList.cs index fa782f1b..8800b8be 100644 --- a/CSharpMath.Editor/Extensions/MathList.cs +++ b/CSharpMath.Editor/Extensions/MathList.cs @@ -131,8 +131,16 @@ void RemoveAtInnerScript(ref MathListIndex index, MathAtom atom, bool superscrip case MathListSubIndexType.None: if (index.AtomIndex == -1) { index = index.Next; - if (self.Atoms[index.AtomIndex] is Atoms.Placeholder) + if (self.Atoms[index.AtomIndex] is Atoms.Placeholder { Superscript: var super, Subscript: var sub }) { self.RemoveAt(index.AtomIndex); + var tempIndex = index; + if (!(sub.Count == 1 && sub[0] is Atoms.Placeholder)) + foreach (var s in sub) + self.InsertAndAdvance(ref tempIndex, s, MathListSubIndexType.None); + if (!(super.Count == 1 && super[0] is Atoms.Placeholder)) + foreach (var s in super) + self.InsertAndAdvance(ref tempIndex, s, MathListSubIndexType.None); + } } else self.RemoveAt(index.AtomIndex); break; diff --git a/CSharpMath.Evaluation/Interpret.cs b/CSharpMath.Evaluation/Interpret.cs index 796784b0..f908406b 100644 --- a/CSharpMath.Evaluation/Interpret.cs +++ b/CSharpMath.Evaluation/Interpret.cs @@ -29,7 +29,7 @@ public static string Interpret(Atom.MathList mathList, System.Func // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. From 4ec984c4832de62c9a11cd9551711055566a3a1a Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 28 May 2020 00:29:22 +0800 Subject: [PATCH 10/15] More range checking --- CSharpMath.Editor/Extensions/MathList.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CSharpMath.Editor/Extensions/MathList.cs b/CSharpMath.Editor/Extensions/MathList.cs index 8800b8be..42a234ec 100644 --- a/CSharpMath.Editor/Extensions/MathList.cs +++ b/CSharpMath.Editor/Extensions/MathList.cs @@ -9,7 +9,7 @@ namespace CSharpMath.Editor { partial class Extensions { static void InsertAtAtomIndexAndAdvance(this MathList self, int atomIndex, MathAtom atom, ref MathListIndex advance, MathListSubIndexType advanceType) { if (atomIndex < 0 || atomIndex > self.Count) - throw new IndexOutOfRangeException($"Index {atomIndex} is out of bounds for list of size {self.Atoms.Count}"); + throw new IndexOutOfRangeException($"Insertion index {atomIndex} is out of bounds for list of size {self.Atoms.Count}"); // Test for placeholder to the right of index, e.g. \sqrt{‸■} -> \sqrt{2‸} if (atomIndex < self.Count && self[atomIndex] is Atoms.Placeholder placeholder) { atom.Superscript.Append(placeholder.Superscript); @@ -25,8 +25,8 @@ static void InsertAtAtomIndexAndAdvance(this MathList self, int atomIndex, MathA /// Inserts and modifies to advance to the next position. public static void InsertAndAdvance(this MathList self, ref MathListIndex index, MathAtom atom, MathListSubIndexType advanceType) { index ??= MathListIndex.Level0Index(0); - if (index.AtomIndex > self.Atoms.Count) - throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); + if (index.AtomIndex < 0 || index.AtomIndex > self.Atoms.Count) + throw new IndexOutOfRangeException($"Insertion index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); switch (index.SubIndexType) { case MathListSubIndexType.None: self.InsertAtAtomIndexAndAdvance(index.AtomIndex, atom, ref index, advanceType); @@ -125,8 +125,8 @@ void RemoveAtInnerScript(ref MathListIndex index, MathAtom atom, bool superscrip } else script.RemoveAt(ref index.SubIndex); } - if (index.AtomIndex > self.Atoms.Count) - throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); + if (index.AtomIndex < -1 || index.AtomIndex >= self.Atoms.Count) + throw new IndexOutOfRangeException($"Deletion index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); switch (index.SubIndexType) { case MathListSubIndexType.None: if (index.AtomIndex == -1) { From 9e68e4994786af08a3bdbdbd7a1ab48318b8f966 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Mon, 1 Jun 2020 19:21:41 +0800 Subject: [PATCH 11/15] Going up and down in fractions --- CSharpMath.Editor.Tests/KeyPressTests.cs | 32 ++++- CSharpMath.Editor/MathKeyboard.cs | 118 ++++++++++++++++-- .../Controls/MathKeyboard.xaml | 28 +++-- CSharpMath/Atom/Atoms/Close.cs | 2 + 4 files changed, 157 insertions(+), 23 deletions(-) diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index 807530db..8f88f306 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -11,12 +11,12 @@ namespace CSharpMath.Editor.Tests { public class KeyPressTests { private static readonly TypesettingContext context = TestTypesettingContexts.Instance; static void Test(string latex, K[] inputs) { - var keyboard = new MathKeyboard(context, new TestFont()); + var keyboard = new MathKeyboard(context, new TestFont(20)); keyboard.KeyPress(inputs); Assert.Equal(latex, keyboard.LaTeX); } static void TestEvent(EventInteractor attach, EventInteractor detach, K[] inputs) { - var keyboard = new MathKeyboard(context, new TestFont()); + var keyboard = new MathKeyboard(context, new TestFont(20)); Assert.Raises( h => attach(keyboard, new EventHandler(h)), h => detach(keyboard, new EventHandler(h)), @@ -153,6 +153,8 @@ public void NoDuplicateValues() { [ Theory, T(@"", K.Left, K.Left, K.Left, K.Right, K.Right, K.Right), + T(@"1", K.Left, K.D1), + T(@"1", K.Right, K.D1), T(@"\square ^■2", K.Power, K.Left, K.Left, K.Right, K.Right, K.Right, K.D2, K.Left, K.Left), T(@"+-\times \div ", K.Divide, K.Left, K.Multiply, K.Left, K.Minus, K.Left, K.Plus), T(@"\sin \cos \tan \arcsin \arccos \arctan ", K.ArcSine, K.ArcCosine, K.Left, K.Left, @@ -194,6 +196,32 @@ public void NoDuplicateValues() { ] public void LeftRightNavigation(string latex, params K[] inputs) => Test(latex, inputs); + [ + Theory, + T(@"", K.Up, K.Up, K.Up, K.Down, K.Down, K.Down), + T(@"1", K.Up, K.D1), + T(@"1", K.Down, K.D1), + T(@"\frac{\square }{■}", K.Fraction, K.Down), + T(@"\frac{\square }{1}", K.Fraction, K.Down, K.D1), + T(@"\frac{■}{\square }", K.Fraction, K.Down, K.Up), + T(@"\frac{1}{\square }", K.Fraction, K.Down, K.Up, K.D1), + T(@"\frac{\square }{■}", K.Fraction, K.Down, K.Up, K.Down), + T(@"\frac{\square }{1}", K.Fraction, K.Down, K.Up, K.Down, K.D1), + T(@"\frac{123x456}{y}", K.Fraction, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Down, K.Up, K.SmallX, K.Down, K.SmallY), + T(@"\frac{y}{123x456}", K.Fraction, K.Down, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Up, K.Down, K.SmallX, K.Up, K.SmallY), + T(@"\frac{1234z56}{xy}", K.Fraction, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Down, K.SmallX, K.SmallY, K.Up, K.SmallZ), + T(@"\frac{xy}{1234z56}", K.Fraction, K.Down, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Up, K.SmallX, K.SmallY, K.Down, K.SmallZ), + T(@"\frac{\sqrt{2}}{■}", K.Fraction, K.SquareRoot, K.D2, K.Down), + T(@"\frac{■}{\sqrt{2}}", K.Fraction, K.Down, K.SquareRoot, K.D2, K.Up), + T(@"\frac{\sqrt{789}}{\square }", K.Fraction, K.SquareRoot, K.D8, K.D9, K.Down, K.Up, K.D7), + T(@"\frac{\square }{\sqrt{789}}", K.Fraction, K.Down, K.SquareRoot, K.D8, K.D9, K.Up, K.Down, K.D7), + T(@"2^{ab}c", K.D2, K.Power, K.SmallA, K.Up, K.SmallB, K.Down, K.SmallC), + T(@"2_{ab}c", K.D2, K.Subscript, K.SmallA, K.Down, K.SmallB, K.Up, K.SmallC), + T(@"2_{cab}^{\square }", K.D2, K.Subscript, K.SmallA, K.SmallB, K.Up, K.Power, K.Down, K.SmallC), + T(@"2c_{\square }^{ab}", K.D2, K.Power, K.SmallA, K.SmallB, K.Down, K.Subscript, K.Up, K.SmallC), + ] + public void UpDownNavigation(string latex, params K[] inputs) => Test(latex, inputs); + [ Theory, T(@"", K.Clear, K.Clear, K.Left, K.Left, K.X, K.Clear, K.Right, K.Right, K.Y, K.Clear), diff --git a/CSharpMath.Editor/MathKeyboard.cs b/CSharpMath.Editor/MathKeyboard.cs index 3c6fd48f..06c83911 100644 --- a/CSharpMath.Editor/MathKeyboard.cs +++ b/CSharpMath.Editor/MathKeyboard.cs @@ -61,7 +61,7 @@ public MathKeyboardCaretState CaretState { blinkTimer.Stop(); blinkTimer.Start(); if (value != MathKeyboardCaretState.Hidden && - MathList.AtomAt(_insertionIndex) is Atoms.Placeholder placeholder) + MathList.AtomAt(_insertionIndex) is Atoms.Placeholder placeholder) (placeholder.Nucleus, _caretState) = value == MathKeyboardCaretState.TemporarilyHidden ? ("\u25A1", MathKeyboardCaretState.TemporarilyHidden) @@ -279,8 +279,6 @@ void MoveCursorLeft() { _insertionIndex = prev; break; } - if (_insertionIndex is null) - throw new InvalidOperationException($"{nameof(_insertionIndex)} is null."); if (_insertionIndex.FinalSubIndexType is MathListSubIndexType.BetweenBaseAndScripts) { var prevInd = _insertionIndex.LevelDown(); if (prevInd != null && MathList.AtomAt(prevInd) is Atoms.Placeholder) @@ -292,8 +290,6 @@ void MoveCursorLeft() { } } void MoveCursorRight() { - if (_insertionIndex is null) - throw new InvalidOperationException($"{nameof(_insertionIndex)} is null."); switch (MathList.AtomAt(_insertionIndex)) { case null: //After Count var levelDown = _insertionIndex.LevelDown(); @@ -380,13 +376,116 @@ void MoveCursorRight() { _insertionIndex = _insertionIndex.Next; break; } - if (_insertionIndex is null) - throw new InvalidOperationException($"{nameof(_insertionIndex)} is null."); if (_insertionIndex.FinalSubIndexType is MathListSubIndexType.BetweenBaseAndScripts && MathList.AtomAt(_insertionIndex.LevelDown()) is Atoms.Placeholder) MoveCursorRight(); } - + void MoveCursorUp() { + if (Display is null) RecreateDisplayFromMathList(); + for (var verticalIndex = _insertionIndex; verticalIndex != null; verticalIndex = verticalIndex.LevelDown()) { + switch (verticalIndex.FinalSubIndexType) { + case MathListSubIndexType.Denominator: + var numerator = + verticalIndex.LevelDown()?.LevelUpWithSubIndex(MathListSubIndexType.Numerator, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + var x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var y = + ClosestPointToIndex(numerator)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + return; + case MathListSubIndexType.Subscript: + var levelDown = + verticalIndex.LevelDown() + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + if (MathList.AtomAt(levelDown) is { Superscript: { Count: 0 } } atom) { + var left = + atom is Atoms.Placeholder + ? levelDown + : levelDown.LevelUpWithSubIndex(MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1)); + var leftX = + ClosestPointToIndex(left)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var right = levelDown.Next; + var rightX = + ClosestPointToIndex(right)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = x - leftX < rightX - x ? left : right; + } else { + var superscript = + levelDown?.LevelUpWithSubIndex(MathListSubIndexType.Superscript, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + y = + ClosestPointToIndex(superscript)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + } + return; + } + } + } + void MoveCursorDown() { + if (Display is null) RecreateDisplayFromMathList(); + for (var verticalIndex = _insertionIndex; verticalIndex != null; verticalIndex = verticalIndex.LevelDown()) { + switch (verticalIndex.FinalSubIndexType) { + case MathListSubIndexType.Numerator: + var denominator = + verticalIndex.LevelDown()?.LevelUpWithSubIndex(MathListSubIndexType.Denominator, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + var x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var y = + ClosestPointToIndex(denominator)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + return; + case MathListSubIndexType.Superscript: + var levelDown = + verticalIndex.LevelDown() + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + if (MathList.AtomAt(levelDown) is { Subscript: { Count: 0 } } atom) { + var left = + atom is Atoms.Placeholder + ? levelDown + : levelDown.LevelUpWithSubIndex(MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1)); + var leftX = + ClosestPointToIndex(left)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var right = levelDown.Next; + var rightX = + ClosestPointToIndex(right)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = x - leftX < rightX - x ? left : right; + } else { + var subscript = + levelDown?.LevelUpWithSubIndex(MathListSubIndexType.Subscript, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + y = + ClosestPointToIndex(subscript)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + } + return; + } + } + } void DeleteBackwards() { // delete the last atom from the list if (HasText && _insertionIndex.PreviousOrBeforeWholeList is MathListIndex previous) { @@ -430,10 +529,11 @@ void InsertSymbolName(string name, bool subscript = false, bool superscript = fa } switch (input) { -#warning Unimplemented up/down buttons case MathKeyboardInput.Up: + MoveCursorUp(); break; case MathKeyboardInput.Down: + MoveCursorDown(); break; case MathKeyboardInput.Left: MoveCursorLeft(); diff --git a/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml b/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml index d7538da6..2389f7af 100644 --- a/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml +++ b/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml @@ -21,7 +21,7 @@