Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2bc6e7e
updated gitignore to ignore .vscode folder
hflexgrig Jan 26, 2026
ce02fc8
Created abstraction atom called UnderAnnotation for under annotations…
hflexgrig Jan 26, 2026
d2eabcb
Added basic latex parser for under annotation
hflexgrig Jan 26, 2026
64ee413
Merge branch 'master' into under_annotations
hflexgrig Jan 27, 2026
6ffa3e7
Merge branch 'verybadcat:master' into master
hflexgrig Jan 28, 2026
1a6629b
Created abstraction atom called UnderAnnotation for under annotations…
hflexgrig Jan 26, 2026
ddad85d
Added basic latex parser for under annotation
hflexgrig Jan 26, 2026
c98495b
Merge branch 'under_annotations' of https://github.com/hflexgrig/CSha…
hflexgrig Jan 28, 2026
92a5fb3
commented failing test
hflexgrig Jan 28, 2026
9227ea6
basic display of underbrace
hflexgrig Feb 1, 2026
9c55b71
add underbrace underlist to a latex parser
hflexgrig Feb 2, 2026
e981a2b
Finished implementation
hflexgrig Feb 2, 2026
6109461
Merge remote-tracking branch 'origin/master' into under_annotations
hflexgrig Feb 3, 2026
06c19f8
Implemented GetHorizontalGlyphAssembly
hflexgrig Feb 3, 2026
a0f06ef
ran dotnet format with fixups
hflexgrig Feb 3, 2026
374a442
fixed unittests
hflexgrig Feb 3, 2026
bf2e3cb
Add Typesetter tests for UnderAnnotation
Happypig375 Feb 3, 2026
fd8484b
merged from master and resolved conflicts
hflexgrig Feb 11, 2026
4bf254b
Merge branch 'master' into under_annotations
Happypig375 Feb 12, 2026
ee314a7
Format
Happypig375 Feb 12, 2026
24712ba
Fix head
Happypig375 Feb 12, 2026
d7a00f2
Use ref?
Happypig375 Feb 12, 2026
377f29e
sha?
Happypig375 Feb 12, 2026
3bce9c8
Check out pull request HEAD commit?
Happypig375 Feb 12, 2026
d2ca42e
fetch?
Happypig375 Feb 12, 2026
696dae0
Need origin?
Happypig375 Feb 12, 2026
8451047
Fetch all?
Happypig375 Feb 12, 2026
5fa3866
Use base_ref and head_ref given commit history
Happypig375 Feb 12, 2026
3109e54
Still need origin huh
Happypig375 Feb 12, 2026
2d20ff3
sha?
Happypig375 Feb 12, 2026
4f8f330
pull_request_target?
Happypig375 Feb 12, 2026
7012adf
Fix Labeller?
Happypig375 Feb 12, 2026
906323f
Fix warnings?
Happypig375 Feb 12, 2026
b9242ba
Underbrace rendering tests
Happypig375 Feb 12, 2026
bd680f9
Fix image baselines
Happypig375 Feb 13, 2026
759289a
Separate workflow change to #256
Happypig375 Feb 14, 2026
fb2e6e1
Merge branch 'master' into under_annotations
Happypig375 Feb 15, 2026
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ CSharpMath.Xaml.Tests.NuGet/Test.*.png
/.benchmarkresults
/.nupkgs
/.testcoverage

/.vscode
## END Specifically added for CSharpMath

## Ignore Visual Studio temporary files, build results, and
Expand Down
28 changes: 28 additions & 0 deletions CSharpMath.Core.Tests/Atom/LaTeXParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,34 @@ public void TestUnderline() {
Assert.Equal(@"\underline{2}", LaTeXParser.MathListToLaTeX(list).ToString());
}

/// <summary>
/// Test underbrace without under
/// </summary>
[Fact]
public void TestUnderbrace() {
var list = ParseLaTeX(@"\underbrace{x}");

Assert.Collection(list,
CheckAtom<UnderAnnotation>("\u23df", underBrace =>
Assert.Collection(underBrace.InnerList, CheckAtom<Variable>("x"))
)
);
}

/// <summary>
/// Test underbrace with under (not implemented yet)
/// </summary>
[Fact]
public void TestUnderbraceWithUnder() {
var list = ParseLaTeX(@"\underbrace{x}_{y}");

Assert.Collection(list,
CheckAtom<UnderAnnotation>("\u23df", underBrace =>
Assert.Collection(underBrace.InnerList, CheckAtom<Variable>("x"))
)
);
}

[Fact]
public void TestAccent() {
var list = ParseLaTeX(@"\bar x");
Expand Down
30 changes: 30 additions & 0 deletions CSharpMath/Atom/Atoms/UnderAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace CSharpMath.Atom.Atoms;
/// <summary>
/// Abstract name of under annotations implementation \underbrace, \underbracket etc..
/// </summary>
public sealed class UnderAnnotation : MathAtom, IMathListContainer {
public UnderAnnotation(string value, MathList innerList, MathList? underList = null) : base(value) {
InnerList = innerList;
UnderList = underList;
}

public MathList InnerList { get; }
public MathList? UnderList { get; }

System.Collections.Generic.IEnumerable<MathList> IMathListContainer.InnerLists =>
new[] { InnerList };
public new UnderAnnotation Clone(bool finalize) => (UnderAnnotation)base.Clone(finalize);
protected override MathAtom CloneInside(bool finalize) =>
new UnderAnnotation(Nucleus, InnerList.Clone(finalize), UnderList?.Clone(finalize));
public override bool ScriptsAllowed => true;
//depending on nucleus, DebugString will change to \underbrace , \underbracket etc..
public override string DebugString =>
new System.Text.StringBuilder(@"\underbrace")
.AppendInBracesOrLiteralNull(InnerList.DebugString)
.ToString();
public bool EqualsUnderAnnotation(UnderAnnotation other) =>
EqualsAtom(other) && InnerList.EqualsList(other.InnerList);
public override bool Equals(object obj) =>
obj is UnderAnnotation u ? EqualsUnderAnnotation(u) : false;
public override int GetHashCode() => (base.GetHashCode(), InnerList).GetHashCode();
}
38 changes: 20 additions & 18 deletions CSharpMath/Atom/LaTeXParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private Result<MathList> BuildInternal(bool oneCharOnly, char stopChar = '\0', M
if (error != null) return error;

switch (handlerResult) {
case ({ } /* dummy */, { } atoms): // Atoms producer (pre-styled)
case ( { } /* dummy */, { } atoms): // Atoms producer (pre-styled)
r.Append(atoms);
prevAtom = r.Atoms.LastOrDefault();
if (oneCharOnly)
Expand All @@ -87,7 +87,7 @@ private Result<MathList> BuildInternal(bool oneCharOnly, char stopChar = '\0', M
return @return;
case (null, null): // Atom modifier
continue;
case ({ } resultAtom, null): // Atom producer
case ( { } resultAtom, null): // Atom producer
atom = resultAtom;
break;
}
Expand All @@ -98,8 +98,7 @@ private Result<MathList> BuildInternal(bool oneCharOnly, char stopChar = '\0', M
return r; // we consumed our character.
}
}
return stopChar switch
{
return stopChar switch {
'\0' => r,
'}' => "Missing closing brace",
_ => "Expected character not found: " + stopChar.ToStringInvariant(),
Expand Down Expand Up @@ -329,8 +328,7 @@ public Result<MathAtom> ReadTable
cell.Insert(0, style);
}
}
return delimiters switch
{
return delimiters switch {
(var left, var right) => new Inner(
new Boundary(left),
new MathList(table),
Expand All @@ -346,8 +344,7 @@ public Result<MathAtom> ReadTable
for (int i = 0, j = 0; i < arrayAlignments.Length && j < table.NColumns; i++, j++) {
// TODO: vertical lines in array currently unsupported
while (arrayAlignments[i] == '|') i++;
table.SetAlignment(arrayAlignments[i] switch
{
table.SetAlignment(arrayAlignments[i] switch {
'l' => ColumnAlignment.Left,
'c' => ColumnAlignment.Center,
'r' => ColumnAlignment.Right,
Expand Down Expand Up @@ -520,12 +517,11 @@ static bool MathAtomToLaTeX(MathAtom atom, StringBuilder builder,
builder.Append('{');
MathListToLaTeX(fraction.Numerator, builder, currentFontStyle);
builder.Append(@" \").Append(
(fraction.LeftDelimiter, fraction.RightDelimiter) switch
{
({ Nucleus: null }, { Nucleus: null }) => "atop",
({ Nucleus: "(" }, { Nucleus: ")" }) => "choose",
({ Nucleus: "{" }, { Nucleus: "}" }) => "brace",
({ Nucleus: "[" }, { Nucleus: "]" }) => "brack",
(fraction.LeftDelimiter, fraction.RightDelimiter) switch {
( { Nucleus: null }, { Nucleus: null }) => "atop",
( { Nucleus: "(" }, { Nucleus: ")" }) => "choose",
( { Nucleus: "{" }, { Nucleus: "}" }) => "brace",
( { Nucleus: "[" }, { Nucleus: "]" }) => "brack",
(var left, var right) => $"atopwithdelims{BoundaryToLaTeX(left)}{BoundaryToLaTeX(right)}",
}).Append(' ');
MathListToLaTeX(fraction.Denominator, builder, currentFontStyle);
Expand Down Expand Up @@ -568,8 +564,7 @@ static bool MathAtomToLaTeX(MathAtom atom, StringBuilder builder,
if (table.Environment == "array") {
builder.Append('{');
foreach (var alignment in table.Alignments)
builder.Append(alignment switch
{
builder.Append(alignment switch {
ColumnAlignment.Left => 'l',
ColumnAlignment.Right => 'r',
_ => 'c'
Expand All @@ -586,8 +581,7 @@ static bool MathAtomToLaTeX(MathAtom atom, StringBuilder builder,
// remove the first atom.
cell = cell.Slice(1, cell.Count - 1);
}
if (table.Environment switch
{
if (table.Environment switch {
"eqalign" => true,
"aligned" => true,
"split" => true,
Expand Down Expand Up @@ -624,6 +618,14 @@ static bool MathAtomToLaTeX(MathAtom atom, StringBuilder builder,
builder.Append(@"\underline{");
MathListToLaTeX(under.InnerList, builder, currentFontStyle);
builder.Append('}');
break;
case UnderAnnotation underAnotation:
if (MathAtomToLaTeX(underAnotation, builder, out var underAnnotationCommand)) {
builder.Append(@$"\{underAnnotationCommand}{{");
MathListToLaTeX(underAnotation.InnerList, builder, currentFontStyle);
builder.Append('}');
}

break;
case Accent accent:
MathAtomToLaTeX(accent, builder, out _);
Expand Down
3 changes: 3 additions & 0 deletions CSharpMath/Atom/LaTeXSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ public static class LaTeXSettings {
parser.ReadArgument().Bind(mathList => Ok(new Overline(mathList))) },
{ @"\underline", (parser, accumulate, stopChar) =>
parser.ReadArgument().Bind(mathList => Ok(new Underline(mathList))) },
{ @"\underbrace", (parser, accumulate, stopChar) =>
parser.ReadArgument().Bind(mathList => Ok(new UnderAnnotation("\u23df", mathList)))},
//Adding under element should happen when adding sub script later or here?
{ @"\begin", (parser, accumulate, stopChar) =>
parser.ReadEnvironment().Bind(env =>
parser.ReadTable(env, null, false, stopChar)).Bind(Ok) },
Expand Down