diff --git a/CHANGELOG.md b/CHANGELOG.md
index fad0db4..97e5aa9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ Improvements
- Exposed Camera's RoundPosition
- Exposed the epsilon value used by Camera
- Added Padding.Empty
+- Throw an exception when text formatter macros resolve recursively too many times
### MLEM.Ui
Additions
diff --git a/MLEM/Formatting/TextFormatter.cs b/MLEM/Formatting/TextFormatter.cs
index f7584c7..ad1fb9b 100644
--- a/MLEM/Formatting/TextFormatter.cs
+++ b/MLEM/Formatting/TextFormatter.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -114,6 +115,7 @@ namespace MLEM.Formatting {
/// The final, recursively resolved string
public string ResolveMacros(string s) {
// resolve macros that resolve into macros
+ var rec = 0;
bool matched;
do {
matched = false;
@@ -124,6 +126,9 @@ namespace MLEM.Formatting {
return macro.Value(this, m, macro.Key);
});
}
+ rec++;
+ if (rec >= 16)
+ throw new ArithmeticException($"A string resolved macros recursively too many times. Does it contain any conflicting macros?\n{s}");
} while (matched);
return s;
}
diff --git a/Tests/FontTests.cs b/Tests/FontTests.cs
index 6ac1366..3ab2998 100644
--- a/Tests/FontTests.cs
+++ b/Tests/FontTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Text.RegularExpressions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -107,6 +108,11 @@ namespace Tests {
const string strg = "This text uses a bunch of non-breaking~spaces to see if macros work. Additionally, it uses a macro that resolves into a bunch of other macros and then, at the end, into text.";
const string goal = "This text uses a bunch of non-breaking\u00A0spaces to see if macros work. Additionally, it uses a macro that resolves into a bunch of other macros and then, at the end, into blue text.";
Assert.AreEqual(this.formatter.ResolveMacros(strg), goal);
+
+ // test recursive macros
+ this.formatter.Macros.Add(new Regex(""), (f, m, r) => "");
+ this.formatter.Macros.Add(new Regex(""), (f, m, r) => "");
+ Assert.Throws(() => this.formatter.ResolveMacros("Test string"));
}
[Test]