public void ForAsciiHandlesAllInputs() { for (sbyte i = -36 + 1; i != -36; i++) // Break loop at arbitrary negative value (-36) { switch (i) { case var _ when i < 0: Assert.Throws <System.ArgumentOutOfRangeException>( () => LaTeXSettings.ForAscii(i) ); break; case var _ when i <= ' ': case (sbyte)'\u007F': case (sbyte)'$': case (sbyte)'%': case (sbyte)'#': case (sbyte)'&': case (sbyte)'~': case (sbyte)'\'': case (sbyte)'^': case (sbyte)'_': case (sbyte)'{': case (sbyte)'}': case (sbyte)'\\': Assert.Null(LaTeXSettings.ForAscii(i)); break; default: Assert.NotNull(LaTeXSettings.ForAscii(i)); break; } } }
public void CommandForAtomIgnoresInnerLists() { var atom = new Atoms.Accent("\u0308", new MathList(new Atoms.Number("1"))); atom.Superscript.Add(new Atoms.Number("4")); atom.Subscript.Add(new Atoms.Variable("x")); Assert.Equal("ddot", LaTeXSettings.CommandForAtom(atom)); }
public void AtomForCommandGeneratesACopy() { var atom = LaTeXSettings.AtomForCommand("int"); if (atom == null) { throw new Xunit.Sdk.NotNullException(); } atom.IndexRange = Range.NotFound; var atom2 = LaTeXSettings.AtomForCommand("int"); if (atom2 == null) { throw new Xunit.Sdk.NotNullException(); } Assert.Equal(Range.Zero, atom2.IndexRange); }
/* //Paste this into the C# Interactive, fill <username> yourself #r "C:/Users/<username>/source/repos/CSharpMath/Typography/Build/NetStandard/Typography.TextBreak/bin/Debug/netstandard1.3/Typography.TextBreak.dll" * using Typography.TextBreak; * (int, WordKind, char)[] BreakText(string text) { * var breaker = new CustomBreaker(); * var breakList = new List<BreakAtInfo>(); * breaker.BreakWords(text); * breaker.LoadBreakAtList(breakList); * //index is after the boundary -> last one will be out of range * return breakList.Select(i => (i.breakAt, i.wordKind, text.ElementAtOrDefault(i.breakAt))).ToArray(); * } * BreakText(@"Here are some text $1 + 12 \frac23 \sqrt4$ $$Display$$ text") */ /* //Version 2 #r "C:/Users/<username>/source/repos/CSharpMath/Typography/Build/NetStandard/Typography.TextBreak/bin/Debug/netstandard1.3/Typography.TextBreak.dll" * using Typography.TextBreak; * string BreakText(string text, string seperator = "|") * { * var breaker = new CustomBreaker(); * var breakList = new List<BreakAtInfo>(); * breaker.BreakWords(text); * breaker.LoadBreakAtList(breakList); * //reverse to ensure earlier inserts do not affect later ones * foreach (var @break in breakList.Select(i => i.breakAt).Reverse()) * text = text.Insert(@break, seperator); * return text; * } * BreakText(@"Here are some text $1 + 12 \frac23 \sqrt4$ $$Display$$ text") */ public static Result <TextAtom> TextAtomFromLaTeX(string latexSource) { if (string.IsNullOrEmpty(latexSource)) { return(new TextAtom.List(Array.Empty <TextAtom>())); } int endAt = 0; bool?displayMath = null; var mathLaTeX = new StringBuilder(); bool backslashEscape = false; bool afterCommand = false; // ignore spaces after command bool afterNewline = false; int dollarCount = 0; var globalAtoms = new TextAtomListBuilder(); List <BreakAtInfo> breakList = new List <BreakAtInfo>(); // Roslyn bug that assumes breakList is nullable resulting in warnings so var is not used var breaker = new CustomBreaker(v => breakList.Add(new BreakAtInfo(v.LatestBreakAt, v.LatestWordKind))) { BreakNumberAfterText = true, ThrowIfCharOutOfRange = false }; breaker.BreakWords(latexSource); Result CheckDollarCount(int startAt, ref int endAt, TextAtomListBuilder atoms) { switch (dollarCount) { case 0: break; case 1: dollarCount = 0; switch (displayMath) { case true: return("Cannot close display math mode with $"); case false: if (atoms.Math(mathLaTeX.ToString(), false, startAt, ref endAt).Error is string error) { return(error); } mathLaTeX.Clear(); displayMath = null; break; case null: displayMath = false; break; } break; case 2: dollarCount = 0; switch (displayMath) { case true: if (atoms.Math(mathLaTeX.ToString(), true, startAt - 1, ref endAt).Error is string error) { return(error); } mathLaTeX.Clear(); displayMath = null; break; case false: return("Cannot close inline math mode with $$"); case null: displayMath = true; break; } break; default: return("Invalid number of $: " + dollarCount); } return(Ok()); } Result <int> BuildBreakList(ReadOnlySpan <char> latex, TextAtomListBuilder atoms, int i, bool oneCharOnly, char stopChar) { void ParagraphBreak() { atoms.Break(); atoms.Space(Space.ParagraphIndent); } for (; i < breakList.Count; i++) { void ObtainSection(ReadOnlySpan <char> latexInput, int index, out int start, out int end, out ReadOnlySpan <char> section, out WordKind kind) { (start, end) = (index == 0 ? 0 : breakList[index - 1].breakAt, breakList[index].breakAt); section = latexInput.Slice(start, end - start); kind = breakList[index].wordKind; } ObtainSection(latex, i, out var startAt, out endAt, out var textSection, out var wordKind); bool NextSection(ReadOnlySpan <char> latexInput, ref ReadOnlySpan <char> section) { bool success = ++i < breakList.Count; if (success) { ObtainSection(latexInput, i, out startAt, out endAt, out section, out wordKind); } return(success); } Result <TextAtom> ReadArgumentAtom(ReadOnlySpan <char> latexInput) { backslashEscape = false; var argAtoms = new TextAtomListBuilder(); return(BuildBreakList(latexInput, argAtoms, ++i, true, '\0') .Bind(index => { i = index; return argAtoms.Build(); })); } SpanResult <char> ReadArgumentString(ReadOnlySpan <char> latexInput, ref ReadOnlySpan <char> section) { afterCommand = false; if (!NextSection(latexInput, ref section) || section.IsNot('{')) { return(Err("Missing {")); } int endingIndex = -1; //startAt + 1 to not start at the { we started at bool isEscape = false; for (int j = startAt + 1, bracketDepth = 0; j < latexInput.Length; j++) { if (latexInput[j] == '\\') { isEscape = true; } else if (latexInput[j] == '{' && !isEscape) { bracketDepth++; } else if (latexInput[j] == '}' && !isEscape) { if (bracketDepth > 0) { bracketDepth--; } else { endingIndex = j; break; } } else { isEscape = false; } } if (endingIndex == -1) { return(Err("Missing }")); } var resultText = latexInput.Slice(endAt, endingIndex - endAt); while (startAt < endingIndex) { _ = NextSection(latexInput, ref section); //this never fails because the above check } return(Ok(resultText)); } Result <Color> ReadColor(ReadOnlySpan <char> latexInput, ref ReadOnlySpan <char> section) => ReadArgumentString(latexInput, ref section).Bind(color => LaTeXSettings.ParseColor(color.ToString()) is Color value ? Ok(value) : Err("Invalid color: " + color.ToString()) ); //Nothing should be before dollar sign checking -- dollar sign checking uses continue; atoms.TextLength = startAt; if (textSection.Is('$')) { if (backslashEscape) { if (displayMath != null) { mathLaTeX.Append(@"\$"); } else { atoms.Text("$"); } } else { dollarCount++; continue; } backslashEscape = false; } else { { if (CheckDollarCount(startAt, ref endAt, atoms).Error is string error) { return(error); } } switch (backslashEscape, displayMath) {