Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Punctuation should stick to inline maths #119

Merged
merged 4 commits into from
Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions CSharpMath.Rendering.Tests/TestRenderingTextData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public sealed class TestRenderingTextData : TestRenderingSharedData<TestRenderin
public const string QuadraticPolynomial = @"$$p(x)=ax^2+bx+c, \text{ where } a \neq 0$$ Above is the definition of a quadratic ($2^{nd}$ degree) polynomial.";
public const string InlineMath = @"$\int_{a_1^2}^{a_2^2}\sqrt\frac x2dx$";
public const string DisplayMath = @"$$\int_{a_1^2}^{a_2^2}\sqrt\frac x2dx$$";
public const string PunctuationAffectsLineBreaking = @"11111222223333344444555556666677777888889999900000 12345678.\\11111222223333344444555556666677777888889999900000 123456789.\\11111222223333344444555556666677777888889999900000 \(\overline{12345678}$.\\11111222223333344444555556666677777888889999900000 $\overline{123456789}\).";
#warning Fix plz
//public const string MatrixLookalike = @"$$x$$ $$y$$ $$z$$";
//public const string MultilineDisplayMath = @"$$x$$\;$$y$$\;$$z$$";
Expand Down
Binary file modified CSharpMath.Rendering.Tests/Text/IntegrationByParts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 55 additions & 8 deletions CSharpMath.Rendering.Text.Tests/TextLaTeXParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ private Action<TextAtom> CheckAtom<T>
[InlineData("123.456")]
[InlineData("abc")]
[InlineData("abc", "123")]
[InlineData("123", "abc")]
[InlineData("12", "a.m.")]
[InlineData("1,", "2,", "3")]
[InlineData("1,,", "2,,", "3")]
[InlineData("1,,,")]
[InlineData("1,,,", "2")]
[InlineData("1()", "a")]
[InlineData("a,", "b,", "c")]
[InlineData("a,.", "b,.", "c")]
[InlineData("a,,", "b,,", "c")]
[InlineData("1/", "2/", "3")]
[InlineData("!@*()")]
Expand Down Expand Up @@ -57,6 +59,51 @@ public void Command(string input, string output, params string[] text) {
Assert.Equal(output, TextLaTeXParser.TextAtomToLaTeX(atom).ToString());
}
[Theory]
[InlineData(@"\color{red}␣a", "a", null, @"\color{red}{a}")]
[InlineData(@"\color{red}␣{a}", "a", null, @"\color{red}{a}")]
[InlineData(@"\color{red}␣😀", "😀", null, @"\color{red}{😀}")]
[InlineData(@"\color{red}␣{😀}", "😀", null, @"\color{red}{😀}")]
[InlineData(@"\color{red}␣\textbar", "|", null, @"\color{red}{\textbar }")]
[InlineData(@"\color{red}␣{\textbar}", "|", null, @"\color{red}{\textbar }")]
[InlineData(@"\color{red}␣aa", "a", "a", @"\color{red}{a}a")]
[InlineData(@"\color{red}␣{a}a", "a", "a", @"\color{red}{a}a")]
[InlineData(@"\color{red}␣a😄", "a", "😄", @"\color{red}{a}😄")]
[InlineData(@"\color{red}␣{a}😄", "a", "😄", @"\color{red}{a}😄")]
[InlineData(@"\color{red}␣😀a", "😀", "a", @"\color{red}{😀}a")]
[InlineData(@"\color{red}␣{😀}a", "😀", "a", @"\color{red}{😀}a")]
[InlineData(@"\color{red}␣😀😄", "😀", "😄", @"\color{red}{😀}😄")]
[InlineData(@"\color{red}␣{😀}😄", "😀", "😄", @"\color{red}{😀}😄")]
[InlineData(@"\color{red}␣\textbar a", "|", "a", @"\color{red}{\textbar }a")]
[InlineData(@"\color{red}␣{\textbar}a", "|", "a", @"\color{red}{\textbar }a")]
[InlineData(@"\color{red}␣a\textbar", "a", "|", @"\color{red}{a}\textbar ")]
[InlineData(@"\color{red}␣{a}\textbar", "a", "|", @"\color{red}{a}\textbar ")]
[InlineData(@"\color{red}␣\textbar\textbar", "|", "|", @"\color{red}{\textbar }\textbar ")]
[InlineData(@"\color{red}␣{\textbar}\textbar", "|", "|", @"\color{red}{\textbar }\textbar ")]
[InlineData(@"\color{red}␣\textbar😄", "|", "😄", @"\color{red}{\textbar }😄")]
[InlineData(@"\color{red}␣{\textbar}😄", "|", "😄", @"\color{red}{\textbar }😄")]
[InlineData(@"\color{red}␣😀\textbar", "😀", "|", @"\color{red}{😀}\textbar ")]
[InlineData(@"\color{red}␣{😀}\textbar", "😀", "|", @"\color{red}{😀}\textbar ")]
public void CommandArguments(string input, string colored, string? after, string output) {
void Test(string input) {
var atom = Parse(input);
var list = new List<TextAtom> {
new TextAtom.Color(new TextAtom.Text(colored), Structures.Color.PredefinedColors["red"])
};
if (after != null) list.Add(new TextAtom.Text(after));
Assert.Equal(list.Count == 1 ? list[0] : new TextAtom.List(list), atom);
Assert.Equal(output, TextLaTeXParser.TextAtomToLaTeX(atom).ToString());
}
Test(input.Replace("␣", ""));
Test(input.Replace("␣", " "));
Test(input.Replace("␣", " "));
Test(input.Replace("␣", "\r"));
Test(input.Replace("␣", "\n"));
Test(input.Replace("␣", "\r\n"));
Test(input.Replace("␣", " \r "));
Test(input.Replace("␣", " \n "));
Test(input.Replace("␣", " \r\n "));
}
[Theory]
[InlineData(@"\textbb", Atom.FontStyle.Blackboard)]
[InlineData(@"\textbf", Atom.FontStyle.Bold)]
[InlineData(@"\textrm", Atom.FontStyle.Default)] // Default is converted to Roman
Expand Down Expand Up @@ -234,14 +281,14 @@ public void Accent(string command, string accent) {
[InlineData(@"\$\[\$\]\$", @"\$", @"\$", true, @"\$", @"\$\[\$ \]\$")]

// https://github.com/verybadcat/CSharpMath/issues/113
//[InlineData(@",$,$,", @",", @",,", false, @"", @",\(,, \)")]
//[InlineData(@",\(,$,", @",", @",,", false, @"", @",\(,, \)")]
//[InlineData(@",$,\),", @",", @",,", false, @"", @",\(,, \)")]
//[InlineData(@",\(,\),", @",", @",,", false, @"", @",\(,, \)")]
//[InlineData(@",$$,$$,", @",", @",,", true, @"", @",\[,, \]")]
//[InlineData(@",\[,$$,", @",", @",,", true, @"", @",\[,, \]")]
//[InlineData(@",$$,\],", @",", @",,", true, @"", @",\[,, \]")]
//[InlineData(@",\[,\],", @",", @",,", true, @"", @",\[,, \]")]
[InlineData(@",$,$,", @",", @",,", false, null, @",\(,,\)")]
[InlineData(@",\(,$,", @",", @",,", false, null, @",\(,,\)")]
[InlineData(@",$,\),", @",", @",,", false, null, @",\(,,\)")]
[InlineData(@",\(,\),", @",", @",,", false, null, @",\(,,\)")]
[InlineData(@",$$,$$,", @",", @",", true, @",", @",\[,\],")]
[InlineData(@",\[,$$,", @",", @",", true, @",", @",\[,\],")]
[InlineData(@",$$,\],", @",", @",", true, @",", @",\[,\],")]
[InlineData(@",\[,\],", @",", @",", true, @",", @",\[,\],")]
public void Math(string input, string? textBefore, string math, bool display, string? textAfter, string output) {
var atom = Parse(input);
var list = new List<TextAtom>();
Expand Down
17 changes: 13 additions & 4 deletions CSharpMath.Rendering/Text/TextAtomListBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ public class TextAtomListBuilder : IReadOnlyList<TextAtom> {
readonly List<TextAtom> _list = new List<TextAtom>();
private void Add(TextAtom atom) => _list.Add(atom);
public void ControlSpace() => Add(new TextAtom.ControlSpace());
public void Accent(TextAtom atom, string accent) =>
Add(new TextAtom.Accent(atom, accent));
public void Text(string text, ReadOnlySpan<char> lookAheadForPunc) =>
Add(new TextAtom.Text(text + lookAheadForPunc.ToString()));
public void Accent(TextAtom atom, string accent) => Add(new TextAtom.Accent(atom, accent));
public void Text(string text) {
if (char.IsPunctuation(text, 0))
switch (Last) {
case TextAtom.Text { Content: var prevText }:
Last = new TextAtom.Text(prevText + text);
return;
case TextAtom.Math { DisplayStyle: false, Content: var mathList }:
mathList.Add(new Atom.Atoms.Punctuation(text));
return;
}
Add(new TextAtom.Text(text));
}
public void Space(Space space) => Add(new TextAtom.Space(space));
public void Style(TextAtom atom, Atom.FontStyle style) => Add(new TextAtom.Style(atom, style));
public void Size(TextAtom atom, float fontSize) => Add(new TextAtom.Size(atom, fontSize));
Expand Down
40 changes: 12 additions & 28 deletions CSharpMath.Rendering/Text/TextLaTeXParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,6 @@ void ObtainSection(ReadOnlySpan<char> latexInput, int index,
kind = breakList[index].wordKind;
}
ObtainSection(latex, i, out var startAt, out endAt, out var textSection, out var wordKind);
bool PreviousSection(ReadOnlySpan<char> latexInput, ref ReadOnlySpan<char> section) {
bool success = i-- > 0;
if (success) ObtainSection(latexInput, i, out startAt, out endAt, out section, out wordKind);
return success;
}
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);
Expand Down Expand Up @@ -155,24 +150,12 @@ Result<Color> ReadColor(ReadOnlySpan<char> latexInput, ref ReadOnlySpan<char> se
Ok(value) :
Err("Invalid color: " + color.ToString())
);
///<summary>Get punctutation after current section</summary>
ReadOnlySpan<char> NextSectionWhilePunc(ReadOnlySpan<char> latexInput, ref ReadOnlySpan<char> section) {
int start = endAt;
ReadOnlySpan<char> specialChars = stackalloc[] { '#', '$', '%', '&', '\\', '^', '_', '{', '}', '~' };
while (NextSection(latexInput, ref section))
if (wordKind != WordKind.Punc || specialChars.IndexOf(section[0]) != -1) {
// We have overlooked by one when non-punctuation or special character is encountered
PreviousSection(latexInput, ref section);
break;
}
return latexInput.Slice(start, endAt - start);
}
//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("$", NextSectionWhilePunc(latex, ref textSection));
else atoms.Text("$");
else {
dollarCount++;
continue;
Expand Down Expand Up @@ -227,7 +210,9 @@ ReadOnlySpan<char> NextSectionWhilePunc(ReadOnlySpan<char> latexInput, ref ReadO
break;
case var _ when textSection.Is('}'):
return "Missing opening brace";
case var _ when wordKind == WordKind.NewLine:
// !char.IsSurrogate(textSection[0]) is result of https://github.com/LayoutFarm/Typography/issues/206
case var _ when wordKind == WordKind.NewLine && !char.IsSurrogate(textSection[0]):
if (oneCharOnly) continue;
// Consume newlines after commands
// Double newline == paragraph break
if (afterNewline) {
Expand All @@ -239,21 +224,20 @@ ReadOnlySpan<char> NextSectionWhilePunc(ReadOnlySpan<char> latexInput, ref ReadO
afterNewline = true;
continue;
}
case var _ when wordKind == WordKind.Whitespace:
case var _ when wordKind == WordKind.Whitespace && !char.IsSurrogate(textSection[0]):
//Collpase spaces
if (afterCommand) continue;
if (afterCommand || oneCharOnly) continue;
else atoms.ControlSpace();
break;
default: //Just ordinary text
if (oneCharOnly) {
if (startAt + 1 < endAt) { //Only re-read if current break span is more than 1 long
var firstCodepointLength = char.IsHighSurrogate(textSection[0]) ? 2 : 1;
if (startAt + firstCodepointLength < endAt) { //Only re-read if current break span is more than 1 long
i--;
breakList[i] = new BreakAtInfo(breakList[i].breakAt + 1, breakList[i].wordKind);
breakList[i] = new BreakAtInfo(breakList[i].breakAt + firstCodepointLength, breakList[i].wordKind);
}
//Need to allocate in the end :(
//Don't look ahead for punc; we are looking for one char only
atoms.Text(textSection[0].ToString(), default);
} else atoms.Text(textSection.ToString(), NextSectionWhilePunc(latex, ref textSection));
atoms.Text(textSection.Slice(0, firstCodepointLength).ToString());
} else atoms.Text(textSection.ToString());
break;
}
afterCommand = false;
Expand Down Expand Up @@ -428,7 +412,7 @@ ReadOnlySpan<char> NextSectionWhilePunc(ReadOnlySpan<char> latexInput, ref ReadO
}
//case "textasciicircum", "textless", ...
case var textSymbol when TextLaTeXSettings.PredefinedTextSymbols.TryGetValue(textSymbol, out var replaceResult):
atoms.Text(replaceResult, NextSectionWhilePunc(latex, ref textSection));
atoms.Text(replaceResult);
break;
case var command:
if (displayMath != null) mathLaTeX.Append(command); //don't eat the command when parsing math
Expand Down