Explorar o código

Fixes #1445. Fixing more the Curses and WSL clipboard. (#1448)

* Fixes #1445. Fixing more the Curses and WSL clipboard.

* Fixing unit tests.

* Changing namespace.

* Fixes WSL2 clipboard unit test.

* Upgrades devcontainer with the MainLoop fix.

* Fixes pasting with no selection and with lines break.

* Prevents the event button click being fired after a button pressed with mouse move.

* Fixes the char [ not being processed.
BDisp %!s(int64=3) %!d(string=hai) anos
pai
achega
84f79b2326

+ 4 - 3
.devcontainer/devcontainer.json

@@ -1,6 +1,6 @@
 {
 	"name": "Terminal.Gui Codespace",
-	"image": "mcr.microsoft.com/vscode/devcontainers/dotnet:0.201.7-5.0",
+	"image": "mcr.microsoft.com/vscode/devcontainers/dotnet:5.0",
 	"settings": {
 		"terminal.integrated.defaultProfile.linux": "pwsh"
 	},
@@ -16,9 +16,10 @@
 		"jmrog.vscode-nuget-package-manager",
 		"coenraads.bracket-pair-colorizer",
 		"vscode-icons-team.vscode-icons",
-		"editorconfig.editorconfig"
+		"editorconfig.editorconfig",
+		"formulahendry.dotnet-test-explorer"
 	],
-	"postCreateCommand": "dotnet restore && dotnet build --configuration Release --no-restore && dotnet test --configuration Debug --no-restore --verbosity normal --collect:'XPlat Code Coverage' --settings UnitTests/coverlet.runsettings",
+	"postCreateCommand": "dotnet restore && dotnet clean && dotnet build --configuration Release --no-restore && dotnet test --configuration Debug --no-restore --verbosity normal --collect:'XPlat Code Coverage' --settings UnitTests/coverlet.runsettings"
 }
 
 // Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)

+ 68 - 23
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -923,7 +923,7 @@ namespace Terminal.Gui {
 
 		public static bool Is_WSL_Platform ()
 		{
-			var result = BashRunner.Run ("uname -a");
+			var result = BashRunner.Run ("uname -a", runCurses: false);
 			if (result.Contains ("microsoft") && result.Contains ("WSL")) {
 				return true;
 			}
@@ -1205,13 +1205,18 @@ namespace Terminal.Gui {
 	}
 
 	class CursesClipboard : ClipboardBase {
-		public override bool IsSupported => CheckSupport ();
+		public CursesClipboard ()
+		{
+			IsSupported = CheckSupport ();
+		}
+
+		public override bool IsSupported { get; }
 
 		bool CheckSupport ()
 		{
 			try {
-				var result = BashRunner.Run ("which xclip");
-				return BashRunner.FileExists (result);
+				var result = BashRunner.Run ("which xclip", runCurses: false);
+				return result.FileExists ();
 			} catch (Exception) {
 				// Permissions issue.
 				return false;
@@ -1246,7 +1251,7 @@ namespace Terminal.Gui {
 	}
 
 	static class BashRunner {
-		public static string Run (string commandLine, bool output = true, string inputText = "")
+		public static string Run (string commandLine, bool output = true, string inputText = "", bool runCurses = true)
 		{
 			var arguments = $"-c \"{commandLine}\"";
 
@@ -1276,6 +1281,10 @@ namespace Terminal.Gui {
 						throw new Exception (timeoutError);
 					}
 					if (process.ExitCode == 0) {
+						if (runCurses && Application.Driver is CursesDriver) {
+							Curses.raw ();
+							Curses.noecho ();
+						}
 						return outputBuilder.ToString ();
 					}
 
@@ -1298,12 +1307,16 @@ namespace Terminal.Gui {
 					process.StandardInput.Write (inputText);
 					process.StandardInput.Close ();
 					process.WaitForExit ();
+					if (runCurses && Application.Driver is CursesDriver) {
+						Curses.raw ();
+						Curses.noecho ();
+					}
 					return inputText;
 				}
 			}
 		}
 
-		static bool DoubleWaitForExit (this System.Diagnostics.Process process)
+		public static bool DoubleWaitForExit (this System.Diagnostics.Process process)
 		{
 			var result = process.WaitForExit (500);
 			if (result) {
@@ -1312,7 +1325,7 @@ namespace Terminal.Gui {
 			return result;
 		}
 
-		public static bool FileExists (string value)
+		public static bool FileExists (this string value)
 		{
 			return !string.IsNullOrEmpty (value) && !value.Contains ("not found");
 		}
@@ -1332,23 +1345,24 @@ namespace Terminal.Gui {
 		IntPtr generalPasteboardRegister = sel_registerName ("generalPasteboard");
 		IntPtr clearContentsRegister = sel_registerName ("clearContents");
 
-		public override bool IsSupported => CheckSupport ();
+		public MacOSXClipboard ()
+		{
+			utfTextType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "public.utf8-plain-text");
+			nsStringPboardType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "NSStringPboardType");
+			generalPasteboard = objc_msgSend (nsPasteboard, generalPasteboardRegister);
+			IsSupported = CheckSupport ();
+		}
+
+		public override bool IsSupported { get; }
 
 		bool CheckSupport ()
 		{
 			var result = BashRunner.Run ("which pbcopy");
-			if (!BashRunner.FileExists (result)) {
+			if (!result.FileExists ()) {
 				return false;
 			}
 			result = BashRunner.Run ("which pbpaste");
-			return BashRunner.FileExists (result);
-		}
-
-		public MacOSXClipboard ()
-		{
-			utfTextType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "public.utf8-plain-text");
-			nsStringPboardType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "NSStringPboardType");
-			generalPasteboard = objc_msgSend (nsPasteboard, generalPasteboardRegister);
+			return result.FileExists ();
 		}
 
 		protected override string GetClipboardDataImpl ()
@@ -1392,19 +1406,28 @@ namespace Terminal.Gui {
 	}
 
 	class WSLClipboard : ClipboardBase {
-		public override bool IsSupported => CheckSupport ();
+		public WSLClipboard ()
+		{
+			IsSupported = CheckSupport ();
+		}
+
+		public override bool IsSupported { get; }
 
 		bool CheckSupport ()
 		{
-			var result = BashRunner.Run ("which powershell.exe");
-			return BashRunner.FileExists (result);
+			try {
+				var result = BashRunner.Run ("which powershell.exe");
+				return result.FileExists ();
+			} catch (System.Exception) {
+				return false;
+			}
 
 			//var result = BashRunner.Run ("which powershell.exe");
-			//if (!BashRunner.FileExists (result)) {
+			//if (!result.FileExists ()) {
 			//	return false;
 			//}
 			//result = BashRunner.Run ("which clip.exe");
-			//return BashRunner.FileExists (result);
+			//return result.FileExists ();
 		}
 
 		protected override string GetClipboardDataImpl ()
@@ -1413,13 +1436,25 @@ namespace Terminal.Gui {
 				StartInfo = new System.Diagnostics.ProcessStartInfo {
 					RedirectStandardOutput = true,
 					FileName = "powershell.exe",
-					Arguments = "-noprofile -command \"Get-Clipboard\""
+					Arguments = "-noprofile -command \"Get-Clipboard\"",
+					UseShellExecute = Application.Driver is CursesDriver,
+					CreateNoWindow = true
 				}
 			}) {
 				powershell.Start ();
 				var result = powershell.StandardOutput.ReadToEnd ();
 				powershell.StandardOutput.Close ();
 				powershell.WaitForExit ();
+				if (!powershell.DoubleWaitForExit ()) {
+					var timeoutError = $@"Process timed out. Command line: bash {powershell.StartInfo.Arguments}.
+							Output: {powershell.StandardOutput.ReadToEnd ()}
+							Error: {powershell.StandardError.ReadToEnd ()}";
+					throw new Exception (timeoutError);
+				}
+				if (Application.Driver is CursesDriver) {
+					Curses.raw ();
+					Curses.noecho ();
+				}
 				return result.TrimEnd ();
 			}
 		}
@@ -1434,6 +1469,16 @@ namespace Terminal.Gui {
 			}) {
 				powershell.Start ();
 				powershell.WaitForExit ();
+				if (!powershell.DoubleWaitForExit ()) {
+					var timeoutError = $@"Process timed out. Command line: bash {powershell.StartInfo.Arguments}.
+							Output: {powershell.StandardOutput.ReadToEnd ()}
+							Error: {powershell.StandardError.ReadToEnd ()}";
+					throw new Exception (timeoutError);
+				}
+				if (Application.Driver is CursesDriver) {
+					Curses.raw ();
+					Curses.noecho ();
+				}
 			}
 
 			//using (var clipExe = new System.Diagnostics.Process {

+ 1 - 1
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -253,7 +253,7 @@ namespace Terminal.Gui {
 				}
 				break;
 			case 27:
-			case 91:
+			//case 91:
 				ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { consoleKeyInfo };
 				ConsoleModifiers mod = consoleKeyInfo.Modifiers;
 				int delay = 0;

+ 10 - 2
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -862,13 +862,14 @@ namespace Terminal.Gui {
 			case WindowsConsole.EventType.Mouse:
 				var me = ToDriverMouse (inputEvent.MouseEvent);
 				mouseHandler (me);
-				if (isButtonReleased) {
+				if (processButtonClick) {
 					mouseHandler (
 						new MouseEvent () {
 							X = me.X,
 							Y = me.Y,
 							Flags = ProcessButtonClick (inputEvent.MouseEvent)
 						});
+					processButtonClick = false;
 				}
 				break;
 
@@ -898,6 +899,7 @@ namespace Terminal.Gui {
 		Point point;
 		int buttonPressedCount;
 		bool isOneFingerDoubleClicked = false;
+		bool processButtonClick;
 
 		MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
 		{
@@ -1008,6 +1010,7 @@ namespace Terminal.Gui {
 					mouseFlag |= MouseFlags.ReportMousePosition;
 					point = new Point ();
 					isButtonReleased = false;
+					processButtonClick = false;
 				} else {
 					point = new Point () {
 						X = mouseEvent.MousePosition.X,
@@ -1850,7 +1853,12 @@ namespace Terminal.Gui {
 	}
 
 	class WindowsClipboard : ClipboardBase {
-		public override bool IsSupported => IsClipboardFormatAvailable (cfUnicodeText) ? true : false;
+		public WindowsClipboard ()
+		{
+			IsSupported = IsClipboardFormatAvailable (cfUnicodeText);
+		}
+
+		public override bool IsSupported { get; }
 
 		protected override string GetClipboardDataImpl ()
 		{

+ 1 - 1
Terminal.Gui/Core/MainLoop.cs

@@ -122,8 +122,8 @@ namespace Terminal.Gui {
 				while (timeouts.ContainsKey (k)) {
 					k = (DateTime.UtcNow + time).Ticks;
 				}
+				timeouts.Add (k, timeout);
 			}
-			timeouts.Add (k, timeout);
 		}
 
 		/// <summary>

+ 12 - 4
Terminal.Gui/Views/TextView.cs

@@ -2738,15 +2738,23 @@ namespace Terminal.Gui {
 			}
 
 			SetWrapModel ();
+			var contents = Clipboard.Contents;
 			if (copyWithoutSelection) {
-				var runeList = Clipboard.Contents == null ? new List<Rune> () : Clipboard.Contents.ToRuneList ();
-				model.AddLine (currentRow, runeList);
-				currentRow++;
+				var runeList = contents == null ? new List<Rune> () : contents.ToRuneList ();
+				if ((runeList?.Count > 0 && runeList [0] == '\n')
+					|| (runeList?.Count > 1 && runeList [0] == '\r'
+					&& runeList [1] == '\n')) {
+
+					InsertText (contents);
+				} else {
+					model.AddLine (currentRow, runeList);
+					currentRow++;
+				}
 			} else {
 				if (selecting) {
 					ClearRegion ();
 				}
-				InsertText (Clipboard.Contents);
+				InsertText (contents);
 				copyWithoutSelection = false;
 			}
 			UpdateWrapModel ();

+ 1 - 1
UnitTests/AutocompleteTests.cs

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 using Terminal.Gui;
 using Xunit;
 
-namespace UnitTests {
+namespace Terminal.Gui.Core {
 	public class AutocompleteTests {
 
 		[Fact][AutoInitShutdown]

+ 18 - 25
UnitTests/ClipboardTests.cs

@@ -5,10 +5,9 @@ using Xunit;
 namespace Terminal.Gui.Core {
 	public class ClipboardTests {
 		[Fact]
+		[AutoInitShutdown]
 		public void Contents_Gets_Sets ()
 		{
-			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
-
 			var clipText = "This is a clipboard unit test.";
 			Clipboard.Contents = clipText;
 
@@ -17,29 +16,23 @@ namespace Terminal.Gui.Core {
 			Application.Run ();
 
 			Assert.Equal (clipText, Clipboard.Contents);
-
-			Application.Shutdown ();
 		}
 
 		[Fact]
+		[AutoInitShutdown]
 		public void IsSupported_Get ()
 		{
-			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
-
 			if (Clipboard.IsSupported) {
 				Assert.True (Clipboard.IsSupported);
 			} else {
 				Assert.False (Clipboard.IsSupported);
 			}
-
-			Application.Shutdown ();
 		}
 
 		[Fact]
+		[AutoInitShutdown]
 		public void TryGetClipboardData_Gets_From_OS_Clipboard ()
 		{
-			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
-
 			var clipText = "Trying to get from the OS clipboard.";
 			Clipboard.Contents = clipText;
 
@@ -54,15 +47,12 @@ namespace Terminal.Gui.Core {
 				Assert.False (Clipboard.TryGetClipboardData (out string result));
 				Assert.NotEqual (clipText, result);
 			}
-
-			Application.Shutdown ();
 		}
 
 		[Fact]
+		[AutoInitShutdown]
 		public void TrySetClipboardData_Sets_The_OS_Clipboard ()
 		{
-			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
-
 			var clipText = "Trying to set the OS clipboard.";
 			if (Clipboard.IsSupported) {
 				Assert.True (Clipboard.TrySetClipboardData (clipText));
@@ -79,17 +69,15 @@ namespace Terminal.Gui.Core {
 			} else {
 				Assert.NotEqual (clipText, Clipboard.Contents);
 			}
-
-			Application.Shutdown ();
 		}
 
 		[Fact]
+		[AutoInitShutdown]
 		public void Contents_Gets_From_OS_Clipboard ()
 		{
-			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
-
 			var clipText = "This is a clipboard unit test to get clipboard from OS.";
 			var exit = false;
+			var getClipText = "";
 
 			Application.Iteration += () => {
 				if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
@@ -117,6 +105,8 @@ namespace Terminal.Gui.Core {
 						pwsh.Start ();
 						pwsh.WaitForExit ();
 					}
+					getClipText = Clipboard.Contents.ToString ();
+
 				} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
 					using (Process copy = new Process {
 						StartInfo = new ProcessStartInfo {
@@ -129,6 +119,8 @@ namespace Terminal.Gui.Core {
 						copy.StandardInput.Close ();
 						copy.WaitForExit ();
 					}
+					getClipText = Clipboard.Contents.ToString ();
+
 				} else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) {
 					if (Is_WSL_Platform ()) {
 						try {
@@ -160,6 +152,9 @@ namespace Terminal.Gui.Core {
 						} catch {
 							exit = true;
 						}
+						if (!exit) {
+							getClipText = Clipboard.Contents.ToString ();
+						}
 						Application.RequestStop ();
 						return;
 					}
@@ -181,6 +176,9 @@ namespace Terminal.Gui.Core {
 						bash.StandardInput.Close ();
 						bash.WaitForExit ();
 					}
+					if (!exit) {
+						getClipText = Clipboard.Contents.ToString ();
+					}
 				}
 
 				Application.RequestStop ();
@@ -189,17 +187,14 @@ namespace Terminal.Gui.Core {
 			Application.Run ();
 
 			if (!exit) {
-				Assert.Equal (clipText, Clipboard.Contents);
+				Assert.Equal (clipText, getClipText);
 			}
-
-			Application.Shutdown ();
 		}
 
 		[Fact]
+		[AutoInitShutdown]
 		public void Contents_Sets_The_OS_Clipboard ()
 		{
-			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
-
 			var clipText = "This is a clipboard unit test to set the OS clipboard.";
 			var clipReadText = "";
 			var exit = false;
@@ -279,8 +274,6 @@ namespace Terminal.Gui.Core {
 			if (!exit) {
 				Assert.Equal (clipText, clipReadText);
 			}
-
-			Application.Shutdown ();
 		}
 
 		bool Is_WSL_Platform ()