Browse Source

Fixes #2212. Editor Scenario: Crashes on Exit.

BDisp 2 years ago
parent
commit
a84c682b62

+ 4 - 1
Terminal.Gui/Views/TextField.cs

@@ -235,7 +235,10 @@ namespace Terminal.Gui {
 
 		private void HistoryText_ChangeText (HistoryText.HistoryTextItem obj)
 		{
-			Text = ustring.Make (obj.Lines [obj.CursorPosition.Y]);
+			if (obj == null)
+				return;
+
+			Text = ustring.Make (obj?.Lines [obj.CursorPosition.Y]);
 			CursorPosition = obj.CursorPosition.X;
 			Adjust ();
 		}

+ 18 - 5
Terminal.Gui/Views/TextView.cs

@@ -92,7 +92,7 @@ namespace Terminal.Gui {
 			var line = new List<byte> ();
 			var wasNewLine = false;
 			while ((v = buff.ReadByte ()) != -1) {
-				if (v == 13) {
+				if ((!AllowsNullTerminated && v == '\0') || v == 13) {
 					continue;
 				}
 				if (v == 10) {
@@ -142,6 +142,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public int Count => lines.Count;
 
+		/// <summary>
+		/// Gets or sets if null terminated in a buffer array is considered as part of the <see cref="lines"/>.
+		/// </summary>
+		public bool AllowsNullTerminated { get; set; }
+
 		/// <summary>
 		/// Returns the specified line as a List of Rune
 		/// </summary>
@@ -1165,7 +1170,7 @@ namespace Terminal.Gui {
 		/// Unlike the <see cref="TextChanged"/> event, this event is raised whenever the user types or
 		/// otherwise changes the contents of the <see cref="TextView"/>.
 		/// </remarks>
-		public Action<ContentsChangedEventArgs> ContentsChanged;
+		public event Action<ContentsChangedEventArgs> ContentsChanged;
 
 		/// <summary>
 		/// Invoked with the unwrapped <see cref="CursorPosition"/>.
@@ -1432,7 +1437,7 @@ namespace Terminal.Gui {
 			}
 
 			UpdateWrapModel ();
-			
+
 			Adjust ();
 			OnContentsChanged ();
 		}
@@ -1794,6 +1799,14 @@ namespace Terminal.Gui {
 		/// </summary>
 		public ContextMenu ContextMenu { get; private set; }
 
+		/// <summary>
+		/// Gets or sets if null terminated in a buffer array is considered as part of the <see cref="Text"/>.
+		/// </summary>
+		public bool AllowsNullTerminated {
+			get => model.AllowsNullTerminated;
+			set => model.AllowsNullTerminated = value;
+		}
+
 		int GetSelectedLength ()
 		{
 			return SelectedText.Length;
@@ -1830,6 +1843,7 @@ namespace Terminal.Gui {
 			try {
 				SetWrapModel ();
 				res = model.LoadFile (path);
+				historyText.Clear (Text);
 				ResetPosition ();
 			} catch (Exception) {
 				throw;
@@ -1837,7 +1851,6 @@ namespace Terminal.Gui {
 				UpdateWrapModel ();
 				SetNeedsDisplay ();
 				Adjust ();
-				OnContentsChanged ();
 			}
 			return res;
 		}
@@ -1850,9 +1863,9 @@ namespace Terminal.Gui {
 		public void LoadStream (Stream stream)
 		{
 			model.LoadStream (stream);
+			historyText.Clear (Text);
 			ResetPosition ();
 			SetNeedsDisplay ();
-			OnContentsChanged ();
 		}
 
 		/// <summary>

+ 15 - 0
UnitTests/TextFieldTests.cs

@@ -1281,5 +1281,20 @@ namespace Terminal.Gui.Views {
 			Assert.Equal (0, tf.ScrollOffset);
 			Assert.Equal (16, tf.CursorPosition);
 		}
+
+		[Fact]
+		public void HistoryText_IsDirty_ClearHistoryChanges ()
+		{
+			var text = "Testing";
+			var tf = new TextField (text);
+
+			Assert.Equal (text, tf.Text);
+			tf.ClearHistoryChanges ();
+			Assert.False (tf.IsDirty);
+
+			Assert.True (tf.ProcessKey (new KeyEvent (Key.A, new KeyModifiers ())));
+			Assert.Equal ($"{text}A", tf.Text);
+			Assert.True (tf.IsDirty);
+		}
 	}
 }

+ 78 - 41
UnitTests/TextViewTests.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics.Tracing;
 using System.Linq;
 using System.Reflection;
 using System.Text.RegularExpressions;
@@ -1930,6 +1929,42 @@ namespace Terminal.Gui.Views {
 			Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}", tv.Text);
 		}
 
+		[Fact]
+		public void LoadStream_IsDirty_AllowsNullTerminator ()
+		{
+			var text = "Testing";
+			byte [] buff = System.Text.Encoding.Unicode.GetBytes (text);
+			using (System.IO.MemoryStream stream = new System.IO.MemoryStream (buff, true)) {
+
+				stream.Write (buff, 0, buff.Length);
+				stream.Position = 0;
+
+				var tv = new TextView ();
+				tv.LoadStream (stream);
+
+				Assert.False (tv.AllowsNullTerminated);
+				Assert.Equal (text.Length, tv.Text.Length);
+				Assert.Equal (text, tv.Text);
+				Assert.False (tv.IsDirty);
+			}
+
+			buff = System.Text.Encoding.Unicode.GetBytes (text);
+			using (System.IO.MemoryStream stream = new System.IO.MemoryStream (buff, true)) {
+
+				stream.Write (buff, 0, buff.Length);
+				stream.Position = 0;
+
+				var tv = new TextView ();
+				tv.AllowsNullTerminated = true;
+				tv.LoadStream (stream);
+
+				Assert.Equal (buff.Length, tv.Text.Length);
+				Assert.NotEqual (text, tv.Text);
+				Assert.Equal (buff, tv.Text);
+				Assert.False (tv.IsDirty);
+			}
+		}
+
 		[Fact]
 		public void StringToRunes_Slipts_CRLF ()
 		{
@@ -6430,16 +6465,17 @@ This is the second line.
 		[Fact, AutoInitShutdown]
 		public void ContentsChanged_Event_NoFires_On_CursorPosition ()
 		{
+			var eventcount = 0;
+
 			var tv = new TextView {
 				Width = 50,
 				Height = 10,
 			};
 
-			var eventcount = 0;
-			Assert.Null (tv.ContentsChanged);
 			tv.ContentsChanged += (e) => {
 				eventcount++;
 			};
+			Assert.Equal (0, eventcount);
 
 			tv.CursorPosition = new Point (0, 0);
 
@@ -6449,19 +6485,19 @@ This is the second line.
 		[Fact, AutoInitShutdown]
 		public void ContentsChanged_Event_Fires_On_InsertText ()
 		{
+			var eventcount = 0;
+
 			var tv = new TextView {
 				Width = 50,
 				Height = 10,
 			};
 			tv.CursorPosition = new Point (0, 0);
 
-			var eventcount = 0;
-
-			Assert.Null (tv.ContentsChanged);
 			tv.ContentsChanged += (e) => {
 				eventcount++;
 			};
 
+			Assert.Equal (0, eventcount);
 
 			tv.InsertText ("a");
 			Assert.Equal (1, eventcount);
@@ -6469,7 +6505,7 @@ This is the second line.
 			tv.CursorPosition = new Point (0, 0);
 			tv.InsertText ("bcd");
 			Assert.Equal (4, eventcount);
-			
+
 			tv.InsertText ("e");
 			Assert.Equal (5, eventcount);
 
@@ -6494,11 +6530,11 @@ This is the second line.
 			var tv = new TextView {
 				Width = 50,
 				Height = 10,
-				ContentsChanged = (e) => {
-					eventcount++;
-					Assert.Equal (expectedRow, e.Row);
-					Assert.Equal (expectedCol, e.Col);
-				}
+			};
+			tv.ContentsChanged += (e) => {
+				eventcount++;
+				Assert.Equal (expectedRow, e.Row);
+				Assert.Equal (expectedCol, e.Col);
 			};
 
 			Application.Top.Add (tv);
@@ -6523,12 +6559,13 @@ This is the second line.
 				// you'd think col would be 3, but it's 0 because TextView sets
 				// row/col = 0 when you set Text
 				Text = "abc",
-				ContentsChanged = (e) => {
-					eventcount++;
-					Assert.Equal (expectedRow, e.Row);
-					Assert.Equal (expectedCol, e.Col);
-				}
 			};
+			tv.ContentsChanged += (e) => {
+				eventcount++;
+				Assert.Equal (expectedRow, e.Row);
+				Assert.Equal (expectedCol, e.Col);
+			};
+
 			Assert.Equal ("abc", tv.Text);
 
 			Application.Top.Add (tv);
@@ -6554,11 +6591,11 @@ This is the second line.
 			var tv = new TextView {
 				Width = 50,
 				Height = 10,
-				ContentsChanged = (e) => {
-					eventcount++;
-					Assert.Equal (expectedRow, e.Row);
-					Assert.Equal (expectedCol, e.Col);
-				}
+			};
+			tv.ContentsChanged += (e) => {
+				eventcount++;
+				Assert.Equal (expectedRow, e.Row);
+				Assert.Equal (expectedCol, e.Col);
 			};
 
 			Application.Top.Add (tv);
@@ -6580,7 +6617,7 @@ This is the second line.
 		{
 			var eventcount = 0;
 
-			_textView.ContentsChanged = (e) => {
+			_textView.ContentsChanged += (e) => {
 				eventcount++;
 			};
 
@@ -6601,13 +6638,12 @@ This is the second line.
 			Assert.Equal (expectedEventCount, eventcount);
 		}
 
-
 		[Fact, InitShutdown]
 		public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests ()
 		{
 			var eventcount = 0;
 
-			_textView.ContentsChanged = (e) => {
+			_textView.ContentsChanged += (e) => {
 				eventcount++;
 			};
 
@@ -6656,7 +6692,7 @@ This is the second line.
 			expectedEventCount += 4;
 			Copy_Without_Selection ();
 			Assert.Equal (expectedEventCount, eventcount);
-			
+
 			// reset
 			expectedEventCount += 1;
 			_textView.Text = InitShutdown.txt;
@@ -6673,7 +6709,7 @@ This is the second line.
 			var eventcount = 0;
 			var expectedEventCount = 0;
 
-			_textView.ContentsChanged = (e) => {
+			_textView.ContentsChanged += (e) => {
 				eventcount++;
 			};
 
@@ -6716,9 +6752,9 @@ This is the second line.
 				Width = 50,
 				Height = 10,
 				Text = text,
-				ContentsChanged = (e) => {
-					eventcount++;
-				}
+			};
+			tv.ContentsChanged += (e) => {
+				eventcount++;
 			};
 
 			Assert.True (tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
@@ -6727,46 +6763,47 @@ This is the second line.
 
 			var expectedEventCount = 1; // for ENTER key
 			Assert.Equal (expectedEventCount, eventcount);
-			
+
 			tv.ClearHistoryChanges ();
 			expectedEventCount = 2;
 			Assert.Equal (expectedEventCount, eventcount);
 		}
 
 		[Fact]
-		public void ContentsChanged_Event_Fires_LoadStream ()
+		public void ContentsChanged_Event_Fires_LoadStream_By_Calling_HistoryText_Clear ()
 		{
 			var eventcount = 0;
 
 			var tv = new TextView {
 				Width = 50,
 				Height = 10,
-				ContentsChanged = (e) => {
-					eventcount++;
-				}
+			};
+			tv.ContentsChanged += (e) => {
+				eventcount++;
 			};
 
 			var text = "This is the first line.\r\nThis is the second line.\r\n";
 			tv.LoadStream (new System.IO.MemoryStream (System.Text.Encoding.ASCII.GetBytes (text)));
 			Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}", tv.Text);
-			
+
 			Assert.Equal (1, eventcount);
 		}
 
 		[Fact]
-		public void ContentsChanged_Event_Fires_LoadFile ()
+		public void ContentsChanged_Event_Fires_On_LoadFile_By_Calling_HistoryText_Clear ()
 		{
 			var eventcount = 0;
 
 			var tv = new TextView {
 				Width = 50,
 				Height = 10,
-				ContentsChanged = (e) => {
-					eventcount++;
-				}
 			};
+			tv.ContentsChanged += (e) => {
+				eventcount++;
+			};
+
 			var fileName = "textview.txt";
-			System.IO.File.WriteAllText (fileName, "This is the first line.\r\nThis is the second line.\r\n") ;
+			System.IO.File.WriteAllText (fileName, "This is the first line.\r\nThis is the second line.\r\n");
 
 			tv.LoadFile (fileName);
 			Assert.Equal (1, eventcount);