Browse Source

Merge pull request #785 from tig/fix_763_incorrect_wordwrap

Fix 763 incorrect wordwrap
Charlie Kindel 5 years ago
parent
commit
22ac187f5c

+ 13 - 9
Terminal.Gui/Core/TextFormatter.cs

@@ -162,6 +162,8 @@ namespace Terminal.Gui {
 						runes.RemoveAt (i);
 						runes.RemoveAt (i + 1);
 						i++;
+					} else {
+						runes.RemoveAt (i);
 					}
 					break;
 				}
@@ -182,6 +184,8 @@ namespace Terminal.Gui {
 						runes [i] = (Rune)' ';
 						runes.RemoveAt (i + 1);
 						i++;
+					} else {
+						runes [i] = (Rune)' ';
 					}
 					break;
 				}
@@ -192,7 +196,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Formats the provided text to fit within the width provided using word wrapping.
 		/// </summary>
-		/// <param name="text">The text to word warp</param>
+		/// <param name="text">The text to word wrap</param>
 		/// <param name="width">The width to contrain the text to</param>
 		/// <returns>Returns a list of word wrapped lines.</returns>
 		/// <remarks>
@@ -200,7 +204,7 @@ namespace Terminal.Gui {
 		/// This method does not do any justification.
 		/// </para>
 		/// <para>
-		/// Newlines ('\n' and '\r\n') sequences are honored, adding the appropriate lines to the output.
+		/// This method strips Newline ('\n' and '\r\n') sequences before processing.
 		/// </para>
 		/// </remarks>
 		public static List<ustring> WordWrap (ustring text, int width)
@@ -223,12 +227,15 @@ namespace Terminal.Gui {
 					end -= 1;
 				if (end == start)
 					end = start + width;
-				lines.Add (ustring.Make (runes.GetRange (start, end - start)).TrimSpace ());
+				lines.Add (ustring.Make (runes.GetRange (start, end - start))); 
 				start = end;
+				if (runes[end] == ' ') {
+					start++;
+				}
 			}
 
 			if (start < text.RuneCount) {
-				lines.Add (ustring.Make (runes.GetRange (start, runes.Count - start)).TrimSpace ());
+				lines.Add (ustring.Make (runes.GetRange (start, runes.Count - start)));
 			}
 
 			return lines;
@@ -279,15 +286,13 @@ namespace Terminal.Gui {
 				return text;
 			}
 
-			// TODO: Use ustring
-			var words = text.Split (ustring.Make (' '));// whitespace, StringSplitOptions.RemoveEmptyEntries);
+			var words = text.Split (ustring.Make (' '));
 			int textCount = words.Sum (arg => arg.RuneCount);
 
 			var spaces = words.Length > 1 ? (width - textCount) / (words.Length - 1) : 0;
 			var extras = words.Length > 1 ? (width - textCount) % words.Length : 0;
 
 			var s = new System.Text.StringBuilder ();
-			//s.Append ($"tc={textCount} sp={spaces},x={extras} - ");
 			for (int w = 0; w < words.Length; w++) {
 				var x = words [w];
 				s.Append (x);
@@ -295,7 +300,6 @@ namespace Terminal.Gui {
 					for (int i = 0; i < spaces; i++)
 						s.Append (spaceChar);
 				if (extras > 0) {
-					//s.Append ('_');
 					extras--;
 				}
 			}
@@ -347,7 +351,7 @@ namespace Terminal.Gui {
 			int runeCount = runes.Count;
 			int lp = 0;
 			for (int i = 0; i < runeCount; i++) {
-				Rune c = text [i];
+				Rune c = runes [i];
 				if (c == '\n') {
 					var wrappedLines = WordWrap (ustring.Make (runes.GetRange (lp, i - lp)), width);
 					foreach (var line in wrappedLines) {

+ 40 - 25
UICatalog/Scenarios/TextFormatterDemo.cs

@@ -1,40 +1,54 @@
-using System;
+using NStack;
+using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
 using Terminal.Gui;
+using Rune = System.Rune;
 
 namespace UICatalog {
 	[ScenarioMetadata (Name: "TextFormatter Demo", Description: "Demos and tests the TextFormatter class.")]
 	[ScenarioCategory ("Text")]
 	[ScenarioCategory ("POC")]
 	class TextFormatterDemo : Scenario {
-		public override void Init (Toplevel top, ColorScheme colorScheme)
-		{
-			Application.Init ();
-
-			Top = top;
-			if (Top == null) {
-				Top = Application.Top;
-			}
-			Win = null;
-		}
-
 		public override void Setup ()
 		{
-			Top.Text = "Press CTRL-Q to Quit. This is the Text for the TopLevel View. TextAlignment.Centered was specified. It is intentionally very long to illustrate word wrap.\n" +
-				"<-- There is a new line here to show a hard line break. You should see this text bleed underneath the subviews, which start at Y = 3.";
-			Top.TextAlignment = TextAlignment.Centered;
-			Top.ColorScheme = Colors.Base;
+			// TODO: Move this to another Scenario that specifically tests `Views` that have no subviews.
+			//Top.Text = "Press CTRL-Q to Quit. This is the Text for the TopLevel View. TextAlignment.Centered was specified. It is intentionally very long to illustrate word wrap.\n" +
+			//	"<-- There is a new line here to show a hard line break. You should see this text bleed underneath the subviews, which start at Y = 3.";
+			//Top.TextAlignment = TextAlignment.Centered;
+			//Top.ColorScheme = Colors.Base;
+
+			// Make Win smaller so sizing the window horizontally will make the
+			// labels shrink to zero-width
+			Win.X = 10;
+			Win.Width = Dim.Fill (10);
 
 			string text = "Hello world, how are you today? Pretty neat!\nSecond line\n\nFourth Line.";
 			string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
 
+			Label blockText = new Label () { ColorScheme = Colors.TopLevel, X = 0, Y = 0, Height = 10, Width = Dim.Fill (0) };
+
+			var block = new StringBuilder ();
+			block.AppendLine ("  ▄████  █    ██  ██▓      ▄████▄    ██████ ");
+			block.AppendLine (" ██▒ ▀█▒ ██  ▓██▒▓██▒     ▒██▀ ▀█  ▒██    ▒ ");
+			block.AppendLine ("▒██░▄▄▄░▓██  ▒██░▒██▒     ▒▓█    ▄ ░ ▓██▄   ");
+			block.AppendLine ("░▓█  ██▓▓▓█  ░██░░██░     ▒▓▓▄ ▄██▒  ▒   ██▒");
+			block.AppendLine ("░▒▓███▀▒▒▒█████▓ ░██░ ██▓ ▒ ▓███▀ ░▒██████▒▒");
+			block.AppendLine (" ░▒   ▒ ░▒▓▒ ▒ ▒ ░▓   ▒▓▒ ░ ░▒ ▒  ░▒ ▒▓▒ ▒ ░");
+			block.AppendLine ("  ░   ░ ░░▒░ ░ ░  ▒ ░ ░▒    ░  ▒   ░ ░▒  ░ ░");
+			block.AppendLine ("░ ░   ░  ░░░ ░ ░  ▒ ░ ░   ░        ░  ░  ░  ");
+			block.AppendLine ("      ░    ░      ░    ░  ░ ░            ░  ");
+			block.AppendLine ("                       ░  ░                 "); 
+			blockText.Text = ustring.Make (block.ToString ()); // .Replace(" ", "\u00A0"); // \u00A0 is 'non-breaking space
+			Win.Add (blockText);
+
 			var unicodeCheckBox = new CheckBox ("Unicode", Top.HotKeySpecifier == (Rune)' ') {
 				X = 0,
-				Y = 3,
+				Y = Pos.Bottom (blockText) + 1,
 			};
 
-			Top.Add (unicodeCheckBox);
+			Win.Add (unicodeCheckBox);
 
 			var alignments = Enum.GetValues (typeof (Terminal.Gui.TextAlignment)).Cast<Terminal.Gui.TextAlignment> ().ToList ();
 			var singleLines = new Label [alignments.Count];
@@ -48,22 +62,22 @@ namespace UICatalog {
 			}
 
 			var label = new Label ($"Demonstrating single-line (should clip):") { Y = Pos.Bottom (unicodeCheckBox) + 1 };
-			Top.Add (label);
+			Win.Add (label);
 			foreach (var alignment in alignments) {
 				label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
-				Top.Add (label);
+				Win.Add (label);
 				singleLines [(int)alignment].Y = Pos.Bottom (label);
-				Top.Add (singleLines [(int)alignment]);
+				Win.Add (singleLines [(int)alignment]);
 				label = singleLines [(int)alignment];
 			}
 
 			label = new Label ($"Demonstrating multi-line and word wrap:") { Y = Pos.Bottom (label) };
-			Top.Add (label);
+			Win.Add (label);
 			foreach (var alignment in alignments) {
 				label = new Label ($"{alignment}:") { Y = Pos.Bottom (label) };
-				Top.Add (label);
+				Win.Add (label);
 				multipleLines [(int)alignment].Y = Pos.Bottom (label);
-				Top.Add (multipleLines [(int)alignment]);
+				Win.Add (multipleLines [(int)alignment]);
 				label = multipleLines [(int)alignment];
 			}
 
@@ -75,4 +89,5 @@ namespace UICatalog {
 			};
 		}
 	}
-}
+}
+

+ 6 - 0
UICatalog/UICatalog.cs

@@ -132,6 +132,12 @@ namespace UICatalog {
 
 			StringBuilder aboutMessage = new StringBuilder ();
 			aboutMessage.AppendLine ("UI Catalog is a comprehensive sample library for Terminal.Gui");
+			aboutMessage.AppendLine (@"             _           ");
+			aboutMessage.AppendLine (@"  __ _ _   _(_)  ___ ___ ");
+			aboutMessage.AppendLine (@" / _` | | | | | / __/ __|");
+			aboutMessage.AppendLine (@"| (_| | |_| | || (__\__ \");
+			aboutMessage.AppendLine (@" \__, |\__,_|_(_)___|___/");
+			aboutMessage.AppendLine (@" |___/                   ");
 			aboutMessage.AppendLine ("");
 			aboutMessage.AppendLine ($"Version: {typeof (UICatalogApp).Assembly.GetName ().Version}");
 			aboutMessage.AppendLine ($"Using Terminal.Gui Version: {typeof (Terminal.Gui.Application).Assembly.GetName ().Version}");

+ 214 - 28
UnitTests/TextFormatterTests.cs

@@ -1559,6 +1559,84 @@ namespace Terminal.Gui {
 			Assert.Equal ("ำ", wrappedLines [^1].ToString ());
 		}
 
+		[Fact]
+		public void WordWrap_Unicode_LineWithNonBreakingSpace ()
+		{
+			var text = ustring.Empty;
+			int width = 0;
+			List<ustring> wrappedLines;
+
+			text = "This\u00A0is\u00A0a\u00A0sentence.";
+			width = text.RuneCount;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.True (wrappedLines.Count == 1);
+
+			width = text.RuneCount - 1;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.Equal (ustring.Make (text.ToRunes () [0..(text.RuneCount - 1)]).ToString (), wrappedLines [0].ToString ());
+			Assert.Equal (".", wrappedLines [1].ToString ());
+
+			width = text.RuneCount - 2;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.Equal (ustring.Make (text.ToRunes () [0..(text.RuneCount - 2)]).ToString (), wrappedLines [0].ToString ());
+
+			width = text.RuneCount - 5;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (2, wrappedLines.Count);
+
+			width = (int)Math.Ceiling ((double)(text.RuneCount / 2F));
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.Equal ("This\u00A0is\u00A0a\u00A0", wrappedLines [0].ToString ());
+			Assert.Equal ("sentence.", wrappedLines [1].ToString ());
+
+			width = (int)Math.Ceiling ((double)(text.RuneCount / 3F));
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (3, wrappedLines.Count);
+			Assert.Equal ("This\u00A0is", wrappedLines [0].ToString ());
+			Assert.Equal ("\u00a0a\u00a0sent", wrappedLines [1].ToString ());
+			Assert.Equal ("ence.", wrappedLines [2].ToString ());
+
+			width = (int)Math.Ceiling ((double)(text.RuneCount / 4F));
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (4, wrappedLines.Count);
+
+			width = (int)Math.Ceiling ((double)text.RuneCount / text.RuneCount);
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.Equal (text.RuneCount, wrappedLines.Count);
+			Assert.Equal ("T", wrappedLines [0].ToString ());
+			Assert.Equal ("h", wrappedLines [1].ToString ());
+			Assert.Equal ("i", wrappedLines [2].ToString ());
+			Assert.Equal (".", wrappedLines [^1].ToString ());
+		}
+
+		[Fact]
+		public void WordWrap_Unicode_2LinesWithNonBreakingSpace ()
+		{
+			var text = ustring.Empty;
+			int width = 0;
+			List<ustring> wrappedLines;
+
+			text = "This\u00A0is\n\u00A0a\u00A0sentence.";
+			width = text.RuneCount;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.True (wrappedLines.Count == 1);
+
+			width = text.RuneCount - 1;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+#pragma warning disable xUnit2013 // Do not use equality check to check for collection size.
+			Assert.Equal (1, wrappedLines.Count);
+#pragma warning restore xUnit2013 // Do not use equality check to check for collection size.
+			Assert.Equal ("This\u00A0is\u00A0a\u00A0sentence.", wrappedLines [0].ToString ());
+
+			text = "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence.";
+			width = text.RuneCount;
+			wrappedLines = TextFormatter.WordWrap (text, width);
+			Assert.True (wrappedLines.Count == 1);
+		}
+
 		[Fact]
 		public void WordWrap_NoNewLines ()
 		{
@@ -1658,6 +1736,87 @@ namespace Terminal.Gui {
 			Assert.Equal ("(пÑивеÑ) has words.", wrappedLines [1].ToString ());
 		}
 
+		/// <summary>
+		/// WordWrap strips CRLF
+		/// </summary>
+		[Fact]
+		public void WordWrap_WithNewLines ()
+		{
+			var text = ustring.Empty;
+			int maxWidth = 0;
+			int expectedClippedWidth = 0;
+
+			List<ustring> wrappedLines;
+
+			text = "A sentence has words.\nA paragraph has lines.";
+			maxWidth = text.RuneCount;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+#pragma warning disable xUnit2013 // Do not use equality check to check for collection size.
+			Assert.Equal (1, wrappedLines.Count);
+#pragma warning restore xUnit2013 // Do not use equality check to check for collection size.
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A sentence has words.A paragraph has lines.", wrappedLines [0].ToString ());
+
+			maxWidth = text.RuneCount - 1;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+#pragma warning disable xUnit2013 // Do not use equality check to check for collection size.
+			Assert.Equal (1, wrappedLines.Count);
+#pragma warning restore xUnit2013 // Do not use equality check to check for collection size.
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A sentence has words.A paragraph has lines.", wrappedLines [0].ToString ());
+
+			maxWidth = text.RuneCount - "words.".Length;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A sentence has words.A paragraph has", wrappedLines [0].ToString ());
+			Assert.Equal ("lines.", wrappedLines [1].ToString ());
+
+			// Unicode 
+			text = "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии.";
+			maxWidth = text.RuneCount;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+			Assert.True (wrappedLines.Count == 1);
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии.", wrappedLines [0].ToString ());
+
+			maxWidth = text.RuneCount - 1;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", wrappedLines [0].ToString ());
+			Assert.Equal ("Линии.", wrappedLines [1].ToString ());
+
+			maxWidth = text.RuneCount - "words.".Length;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", wrappedLines [0].ToString ());
+			Assert.Equal ("Линии.", wrappedLines [1].ToString ());
+
+			maxWidth = text.RuneCount - "s words.".Length;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт", wrappedLines [0].ToString ());
+			Assert.Equal ("has Линии.", wrappedLines [1].ToString ());
+
+			maxWidth = text.RuneCount - "веÑ) has words.".Length;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			wrappedLines = TextFormatter.WordWrap (text, maxWidth);
+			Assert.Equal (2, wrappedLines.Count);
+			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
+			Assert.Equal ("A Unicode sentence (пÑивеÑ) has words.A Unicode", wrappedLines [0].ToString ());
+			Assert.Equal ("Пункт has Линии.", wrappedLines [1].ToString ());
+		}
+
 		[Fact]
 		public void WordWrap_Narrow ()
 		{
@@ -1674,16 +1833,12 @@ namespace Terminal.Gui {
 			//Assert.True (wrappedLines.Count == );
 			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
 			Assert.Equal ("A", wrappedLines [0].ToString ());
-			// BUGBUG: WordWrap breaks down with small widths. It should not
-			// the following line should be "sen"...
-			Assert.Equal ("se", wrappedLines [1].ToString ());
-			Assert.Equal ("nte", wrappedLines [2].ToString ());
-			Assert.Equal ("nce", wrappedLines [3].ToString ());
-			Assert.Equal ("ha", wrappedLines [4].ToString ());
-			Assert.Equal ("s", wrappedLines [5].ToString ());
-			Assert.Equal ("wo", wrappedLines [6].ToString ());
-			Assert.Equal ("rds", wrappedLines [7].ToString ());
-			Assert.Equal (".", wrappedLines [^1].ToString ());
+			Assert.Equal ("sen", wrappedLines [1].ToString ());
+			Assert.Equal ("ten", wrappedLines [2].ToString ());
+			Assert.Equal ("ce", wrappedLines [3].ToString ());
+			Assert.Equal ("has", wrappedLines [4].ToString ());
+			Assert.Equal ("wor", wrappedLines [5].ToString ());
+			Assert.Equal ("ds.", wrappedLines [6].ToString ());
 
 			maxWidth = 2;
 			expectedClippedWidth = 2;
@@ -1691,10 +1846,10 @@ namespace Terminal.Gui {
 			//Assert.True (wrappedLines.Count == );
 			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
 			Assert.Equal ("A", wrappedLines [0].ToString ());
-			Assert.Equal ("s", wrappedLines [1].ToString ());
-			Assert.Equal ("en", wrappedLines [2].ToString ());
-			Assert.Equal ("te", wrappedLines [3].ToString ());
-			Assert.Equal (".", wrappedLines [^1].ToString ());
+			Assert.Equal ("se", wrappedLines [1].ToString ());
+			Assert.Equal ("nt", wrappedLines [2].ToString ());
+			Assert.Equal ("en", wrappedLines [3].ToString ());
+			Assert.Equal ("s.", wrappedLines [^1].ToString ());
 
 			maxWidth = 1;
 			expectedClippedWidth = 1;
@@ -1702,12 +1857,9 @@ namespace Terminal.Gui {
 			//Assert.True (wrappedLines.Count == );
 			Assert.True (expectedClippedWidth >= wrappedLines.Max (l => l.RuneCount));
 			Assert.Equal ("A", wrappedLines [0].ToString ());
-			// BUGBUG: WordWrap breaks down with a width of one. It should not
-			// provide blank lines like it does.
-			Assert.Equal ("", wrappedLines [1].ToString ());
-			Assert.Equal ("s", wrappedLines [2].ToString ());
-			Assert.Equal ("e", wrappedLines [3].ToString ());
-			Assert.Equal ("n", wrappedLines [4].ToString ());
+			Assert.Equal ("s", wrappedLines [1].ToString ());
+			Assert.Equal ("e", wrappedLines [2].ToString ());
+			Assert.Equal ("n", wrappedLines [3].ToString ());
 			Assert.Equal (".", wrappedLines [^1].ToString ());
 
 		}
@@ -2008,13 +2160,13 @@ namespace Terminal.Gui {
 			// Unicode
 			// Even # of chars
 			//      0123456789
-			text = "пÑРвРÑ";
+			text = "\u2660пÑРвРÑ";
 
 			maxWidth = text.RuneCount - 1;
 			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
 			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
 			Assert.True (list.Count == 2);
-			Assert.Equal ("пÑРвÐ", list [0]);
+			Assert.Equal ("\u2660пÑРвÐ", list [0]);
 			Assert.Equal ("Ñ", list [1]);
 
 			// no clip
@@ -2022,24 +2174,24 @@ namespace Terminal.Gui {
 			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
 			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
 			Assert.True (list.Count == 1);
-			Assert.Equal ("пÑРвРÑ", list [0]);
+			Assert.Equal ("\u2660пÑРвРÑ", list [0]);
 
 			maxWidth = text.RuneCount + 1;
 			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
 			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
 			Assert.True (list.Count == 1);
-			Assert.Equal ("пÑРвРÑ", list [0]);
+			Assert.Equal ("\u2660пÑРвРÑ", list [0]);
 
 			// Unicode
 			// Odd # of chars
 			//      0123456789
-			text = "Ð ÑРвРÑ";
+			text = "\u2660 ÑРвРÑ";
 
 			maxWidth = text.RuneCount - 1;
 			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
 			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
 			Assert.True (list.Count == 2);
-			Assert.Equal ("Ð ÑРвÐ", list [0]);
+			Assert.Equal ("\u2660 ÑРвÐ", list [0]);
 			Assert.Equal ("Ñ", list [1]);
 
 			// no clip
@@ -2047,14 +2199,48 @@ namespace Terminal.Gui {
 			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
 			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
 			Assert.True (list.Count == 1);
-			Assert.Equal ("Ð ÑРвРÑ", list [0]);
+			Assert.Equal ("\u2660 ÑРвРÑ", list [0]);
 
 			maxWidth = text.RuneCount + 1;
 			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
 			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
 			Assert.True (list.Count == 1);
-			Assert.Equal ("Ð ÑРвРÑ", list [0]);
+			Assert.Equal ("\u2660 ÑРвРÑ", list [0]);
+		}
+
+		[Fact]
+		public void Reformat_Unicode_Wrap_Spaces_NewLines ()
+		{
+			var text = ustring.Empty;
+			var list = new List<ustring> ();
+			var maxWidth = 0;
+			var expectedClippedWidth = 0;
+			var wrap = true;
+
+			// Unicode
+			text = "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464";
+
+			maxWidth = text.RuneCount - 1;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
+			Assert.Equal (2, list.Count);
+			Assert.Equal ("\u2460\u2461\u2462", list [0]);
+			Assert.Equal ("\u2460\u2461\u2462\u2463\u2464", list [1]);
 
+			// no clip
+			maxWidth = text.RuneCount + 0;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
+			Assert.Equal (2, list.Count);
+			Assert.Equal ("\u2460\u2461\u2462", list [0]);
+			Assert.Equal ("\u2460\u2461\u2462\u2463\u2464", list [1]);
+
+			maxWidth = text.RuneCount + 1;
+			expectedClippedWidth = Math.Min (text.RuneCount, maxWidth);
+			list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
+			Assert.Equal (2, list.Count);
+			Assert.Equal ("\u2460\u2461\u2462", list [0]);
+			Assert.Equal ("\u2460\u2461\u2462\u2463\u2464", list [1]);
 		}
 	}
 }