From 022e62514388a72f8d8a21cdfb645e38809848e5 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Sun, 15 Mar 2020 15:51:05 -0400 Subject: [PATCH] fix: Fixed a bug where table width calculations were not considering unprintable characters --- CHANGELOG.md | 1 + ICD.Common.Utils/AnsiUtils.cs | 4 +- .../Extensions/StringExtensions.cs | 51 +++++++++++++++++++ ICD.Common.Utils/TableBuilder.cs | 8 +-- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40c6ed2..7405af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Console uses unicode for table drawing on Net Standard - Using UTC for tracking scheduled events, fixes issues with DST - Using UTC for tracking durations + - Fixed a bug where table width calculations were not considering unprintable characters ## [10.3.0] - 2020-01-20 ### Changed diff --git a/ICD.Common.Utils/AnsiUtils.cs b/ICD.Common.Utils/AnsiUtils.cs index bf9ea2d..a59f2a1 100644 --- a/ICD.Common.Utils/AnsiUtils.cs +++ b/ICD.Common.Utils/AnsiUtils.cs @@ -37,7 +37,7 @@ namespace ICD.Common.Utils /// /// Matches ANSI escape codes, e.g. \x1b[31m and \x1b[30;1m /// - private const string ANSI_PATTERN = "(?'match'\x01b\\[(?'code'[\\d;]+)m)"; + public const string ANSI_REGEX = "(?'match'\x01b\\[(?'code'[\\d;]+)m)"; /// /// Matches ANSI escape codes to HTML styles. @@ -105,7 +105,7 @@ namespace ICD.Common.Utils if (string.IsNullOrEmpty(data)) yield break; - Regex regex = new Regex(ANSI_PATTERN); + Regex regex = new Regex(ANSI_REGEX); Match match = regex.Match(data); // No matches diff --git a/ICD.Common.Utils/Extensions/StringExtensions.cs b/ICD.Common.Utils/Extensions/StringExtensions.cs index 066e984..f785d60 100644 --- a/ICD.Common.Utils/Extensions/StringExtensions.cs +++ b/ICD.Common.Utils/Extensions/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using ICD.Common.Properties; namespace ICD.Common.Utils.Extensions @@ -256,5 +257,55 @@ namespace ICD.Common.Utils.Extensions return hash1 + (hash2 * 1566083941); } } + + /// + /// Strips all of the non-printable characters and control codes from the string. + /// + /// + /// + public static string ToPrintableCharacters([NotNull] this string extends) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + // Strip ANSI escape sequences + extends = Regex.Replace(extends, AnsiUtils.ANSI_REGEX, string.Empty); + + // Strip control characters + extends = Regex.Replace(extends, @"\p{C}+", string.Empty); + + return extends; + } + + /// + /// Gets the printable length of the string. + /// + /// + /// + public static int GetPrintableLength([NotNull] this string extends) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + return extends.ToPrintableCharacters().Length; + } + + /// + /// Pads the string to the number of printable characters. + /// + /// + /// + /// + public static string PadRightPrintable([NotNull] this string extends, int length) + { + if (extends == null) + throw new ArgumentNullException("extends"); + + int printableLength = extends.GetPrintableLength(); + int actualLength = extends.Length; + int delta = actualLength - printableLength; + + return extends.PadRight(length + delta); + } } } diff --git a/ICD.Common.Utils/TableBuilder.cs b/ICD.Common.Utils/TableBuilder.cs index 0a7bc6a..3d86dee 100644 --- a/ICD.Common.Utils/TableBuilder.cs +++ b/ICD.Common.Utils/TableBuilder.cs @@ -169,14 +169,14 @@ namespace ICD.Common.Utils private int GetColumnWidth(int index) { - int titleLength = m_Columns[index].Length + 1; + int titleLength = m_Columns[index].GetPrintableLength() + 1; if (m_Rows.Count == 0) return titleLength; int maxColumnWidth = m_Rows.Except((string[])null) - .Max(x => x[index] != null ? x[index].Length : 0) + 1; + .Max(x => x[index] != null ? x[index].GetPrintableLength() : 0) + 1; - return (titleLength > maxColumnWidth) ? titleLength : maxColumnWidth; + return titleLength > maxColumnWidth ? titleLength : maxColumnWidth; } private void AppendTopSeparator(StringBuilder builder, IList columnWidths) @@ -245,7 +245,7 @@ namespace ICD.Common.Utils builder.Append(' '); string value = row[index] ?? string.Empty; - builder.Append(value.PadRight(columnWidths[index])); + builder.Append(value.PadRightPrintable(columnWidths[index])); if (index < row.Count - 1) builder.Append(VERTICAL);