diff --git a/CHANGELOG.md b/CHANGELOG.md
index 05f4de8..0920713 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added public access to GetValues enumeration extension
- Added extensions for getting JsonReader values as long or ulong
- Added DateTimeUtils methods for creating DateTimes from epoch seconds or milliseconds
- - Added AnsiToHtml converter for visualizing ANSI logs
+ - Added utils for splitting ANSI into spans for conversion to XAML, HTML, etc
### Changed
- Fixed exception trying to get DHCP status of network interfaces on Linux
diff --git a/ICD.Common.Utils.Tests/AnsiUtilsTest.cs b/ICD.Common.Utils.Tests/AnsiUtilsTest.cs
new file mode 100644
index 0000000..123961b
--- /dev/null
+++ b/ICD.Common.Utils.Tests/AnsiUtilsTest.cs
@@ -0,0 +1,27 @@
+using System.Linq;
+using NUnit.Framework;
+
+namespace ICD.Common.Utils.Tests
+{
+ [TestFixture]
+ public sealed class AnsiUtilsTest
+ {
+ [Test]
+ public void ToSpansTest()
+ {
+ string ansi = "\x1b[30mblack\x1b[37mwhite\x1b[0mdefault";
+ AnsiSpan[] spans = AnsiUtils.ToSpans(ansi).ToArray();
+
+ Assert.AreEqual(3, spans.Length);
+
+ Assert.AreEqual("black", spans[0].Text);
+ Assert.AreEqual("30", spans[0].Code);
+
+ Assert.AreEqual("white", spans[1].Text);
+ Assert.AreEqual("37", spans[1].Code);
+
+ Assert.AreEqual("default", spans[2].Text);
+ Assert.AreEqual("0", spans[2].Code);
+ }
+ }
+}
diff --git a/ICD.Common.Utils.Tests/Converters/AnsiToHtmlTest.cs b/ICD.Common.Utils.Tests/Converters/AnsiToHtmlTest.cs
deleted file mode 100644
index 4d14e5a..0000000
--- a/ICD.Common.Utils.Tests/Converters/AnsiToHtmlTest.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using ICD.Common.Utils.Converters;
-using NUnit.Framework;
-
-namespace ICD.Common.Utils.Tests.Converters
-{
- [TestFixture]
- public sealed class AnsiToHtmlTest
- {
- [TestCase("black\x1b[37mwhite",
- @"blackwhite")]
- [TestCase("black\x1b[0mblack",
- @"blackblack")]
- [TestCase("black\x1b[37mwhite\x1b[0m",
- @"blackwhite")]
- [TestCase("\x1b[30mblack\x1b[37mwhite",
- @"blackwhite")]
- public void ConvertTest(string ansi, string expected)
- {
- Assert.AreEqual(expected, AnsiToHtml.Convert(ansi));
- }
- }
-}
diff --git a/ICD.Common.Utils/AnsiUtils.cs b/ICD.Common.Utils/AnsiUtils.cs
index 71f4878..c1724ce 100644
--- a/ICD.Common.Utils/AnsiUtils.cs
+++ b/ICD.Common.Utils/AnsiUtils.cs
@@ -1,4 +1,7 @@
-namespace ICD.Common.Utils
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace ICD.Common.Utils
{
public static class AnsiUtils
{
@@ -13,6 +16,42 @@
public const string CODE_RESET = "\x1b[0m";
+ ///
+ /// Matches ANSI escape codes, e.g. \x1b[31m and \x1b[30;1m
+ ///
+ private const string ANSI_PATTERN = "(?'match'\x01b\\[(?'code'[\\d;]+)m)";
+
+ ///
+ /// Matches ANSI escape codes to HTML styles.
+ /// Color values are taken from PuTTY.
+ ///
+ private static readonly Dictionary s_PuttyColors =
+ new Dictionary
+ {
+ {"30", "000000"}, // Black
+ {"31", "BB0000"}, // Red
+ {"32", "00BB00"}, // Green
+ {"33", "BBBB00"}, // Yellow
+ {"34", "0000BB"}, // Blue
+ {"35", "BB00BB"}, // Magenta
+ {"36", "00BBBB"}, // Cyan
+ {"37", "BBBBBB"}, // White
+
+ {"30;1", "555555"}, // Bright Black
+ {"31;1", "FF5555"}, // Bright Red
+ {"32;1", "55FF55"}, // Bright Green
+ {"33;1", "FFFF55"}, // Bright Yellow
+ {"34;1", "5555FF"}, // Bright Blue
+ {"35;1", "FF55FF"}, // Bright Magenta
+ {"36;1", "55FFFF"}, // Bright Cyan
+ {"37;1", "FFFFFF"}, // Bright White
+ };
+
+ ///
+ /// Gets the color map matching PuTTY.
+ ///
+ public static IDictionary PuttyColors { get { return s_PuttyColors; } }
+
///
/// Constructor.
///
@@ -37,5 +76,73 @@
return string.Format("{0}{1}{2}", code, data, CODE_RESET);
}
+
+ ///
+ /// Splits the given ANSI string into spans.
+ ///
+ ///
+ ///
+ public static IEnumerable ToSpans(string data)
+ {
+ if (string.IsNullOrEmpty(data))
+ yield break;
+
+ Regex regex = new Regex(ANSI_PATTERN);
+ Match match = regex.Match(data);
+
+ // No matches
+ if (!match.Success)
+ yield return new AnsiSpan {Text = data};
+
+ // Find the spans
+ while (match.Success)
+ {
+ // Get the code
+ string code = match.Groups["code"].Value;
+
+ // Get the text
+ Match next = match.NextMatch();
+ int startIndex = match.Index + match.Length;
+ int endIndex = next.Success ? next.Index : data.Length;
+ string text = data.Substring(startIndex, endIndex - startIndex);
+
+ // Build the span
+ if (text.Length > 0)
+ yield return new AnsiSpan { Code = code, Text = text };
+
+ // Loop
+ match = next;
+ }
+ }
+
+ ///
+ /// Removes the bright suffix from the code if present, otherwise appends a bright suffix.
+ ///
+ ///
+ ///
+ public static string InvertBright(string code)
+ {
+ return code.EndsWith(";1")
+ ? code.Substring(0, code.Length - 1)
+ : code + ";1";
+ }
+ }
+
+ public sealed class AnsiSpan
+ {
+ public string Code { get; set; }
+ public string Text { get; set; }
+
+ ///
+ /// Gets the color value for the code.
+ ///
+ ///
+ ///
+ ///
+ public string GetColor(IDictionary colors, bool invertBright)
+ {
+ string code = invertBright ? AnsiUtils.InvertBright(Code) : Code;
+ return colors[code];
+ }
}
}
\ No newline at end of file
diff --git a/ICD.Common.Utils/Converters/AnsiToHtml.cs b/ICD.Common.Utils/Converters/AnsiToHtml.cs
deleted file mode 100644
index a003cf3..0000000
--- a/ICD.Common.Utils/Converters/AnsiToHtml.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System.Collections.Generic;
-
-namespace ICD.Common.Utils.Converters
-{
- public static class AnsiToHtml
- {
- ///
- /// Matches ANSI escape codes, e.g. \x1b[31m and \x1b[30;1m
- ///
- private const string ANSI_PATTERN = "(?'match'\x01b\\[(?'code'[\\d;]+)m)";
-
- private const string CODE_RESET = "0";
-
- ///
- /// Matches ANSI escape codes to HTML styles.
- /// Color values are taken from PuTTY.
- ///
- private static readonly Dictionary s_Colors =
- new Dictionary
- {
- {"30", "color:#000000"}, // Black
- {"31", "color:#BB0000"}, // Red
- {"32", "color:#00BB00"}, // Green
- {"33", "color:#BBBB00"}, // Yellow
- {"34", "color:#0000BB"}, // Blue
- {"35", "color:#BB00BB"}, // Magenta
- {"36", "color:#00BBBB"}, // Cyan
- {"37", "color:#BBBBBB"}, // White
-
- {"30;1", "color:#555555"}, // Bright Black
- {"31;1", "color:#FF5555"}, // Bright Red
- {"32;1", "color:#55FF55"}, // Bright Green
- {"33;1", "color:#FFFF55"}, // Bright Yellow
- {"34;1", "color:#5555FF"}, // Bright Blue
- {"35;1", "color:#FF55FF"}, // Bright Magenta
- {"36;1", "color:#55FFFF"}, // Bright Cyan
- {"37;1", "color:#FFFFFF"}, // Bright White
- };
-
- ///
- /// Converts the input ansi string into html with color attributes.
- ///
- ///
- ///
- public static string Convert(string ansi)
- {
- int depth = 0;
-
- // Hack - Append a reset to close any open spans
- ansi += AnsiUtils.CODE_RESET;
-
- return RegexUtils.ReplaceGroup(ansi, ANSI_PATTERN, "match", match =>
- {
- string code = match.Groups["code"].Value;
-
- // Reset code - close all of the open spans.
- if (code == CODE_RESET)
- {
- string output = StringUtils.Repeat("", depth);
- depth = 0;
- return output;
- }
-
- string style;
- if (!s_Colors.TryGetValue(code, out style))
- return match.Value;
-
- depth++;
- return string.Format("", style);
- });
- }
- }
-}
\ No newline at end of file
diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
index a917c72..7000084 100644
--- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
+++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj
@@ -87,7 +87,6 @@
-
diff --git a/ICD.Common.Utils/RegexUtils.cs b/ICD.Common.Utils/RegexUtils.cs
index 0db86a2..1d20994 100644
--- a/ICD.Common.Utils/RegexUtils.cs
+++ b/ICD.Common.Utils/RegexUtils.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;