From 585d2c26ea002af3b3def55b662e95e5a552764b Mon Sep 17 00:00:00 2001 From: Ryan West Date: Wed, 2 Aug 2023 14:23:27 -0600 Subject: [PATCH 1/2] Add ViClipboardMode Option This option allows the user to choose if they want the ViRegister to use the system clipboard or the default local clipboard. By setting ViClipboardMode to SystemClipboard, the user can easily copy and paste text in the PowerShell prompt using Vi shortcuts in Command mode. --- PSReadLine/Cmdlets.cs | 19 +++++++++++ PSReadLine/Options.cs | 4 +++ PSReadLine/PSReadLine.format.ps1xml | 3 ++ PSReadLine/ViRegister.cs | 51 ++++++++++++++++++++++------- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/PSReadLine/Cmdlets.cs b/PSReadLine/Cmdlets.cs index bc89cc1f9..aa48df9d3 100644 --- a/PSReadLine/Cmdlets.cs +++ b/PSReadLine/Cmdlets.cs @@ -50,6 +50,12 @@ public enum ViMode Command } + public enum ViClipboardMode + { + ViRegister, + SystemClipboard + } + public enum HistorySaveStyle { SaveIncrementally, @@ -342,6 +348,11 @@ public object ContinuationPromptColor /// public ScriptBlock ViModeChangeHandler { get; set; } + /// + /// Which source should be used when copying and pasting in vi edit mode? + /// + public ViClipboardMode ViClipboardMode { get; set; } + /// /// The path to the saved history. /// @@ -789,6 +800,14 @@ public ViModeStyle ViModeIndicator [Parameter] public ScriptBlock ViModeChangeHandler { get; set; } + [Parameter] + public ViClipboardMode ViClipboardMode + { + get => _viClipboardMode.GetValueOrDefault(); + set => _viClipboardMode = value; + } + internal ViClipboardMode? _viClipboardMode; + [Parameter] public PredictionSource PredictionSource { diff --git a/PSReadLine/Options.cs b/PSReadLine/Options.cs index 19f366513..d8687e682 100644 --- a/PSReadLine/Options.cs +++ b/PSReadLine/Options.cs @@ -120,6 +120,10 @@ private void SetOptionsInternal(SetPSReadLineOption options) } Options.ViModeChangeHandler = options.ViModeChangeHandler; } + if (options._viClipboardMode.HasValue) + { + Options.ViClipboardMode = options.ViClipboardMode; + } if (options.HistorySavePath != null) { Options.HistorySavePath = options.HistorySavePath; diff --git a/PSReadLine/PSReadLine.format.ps1xml b/PSReadLine/PSReadLine.format.ps1xml index 5b20c58af..df3768676 100644 --- a/PSReadLine/PSReadLine.format.ps1xml +++ b/PSReadLine/PSReadLine.format.ps1xml @@ -152,6 +152,9 @@ $d = [Microsoft.PowerShell.KeyHandler]::GetGroupingDescription($_.Group) $null -ne $_.ViModeChangeHandler ViModeChangeHandler + + ViClipboardMode + WordDelimiters diff --git a/PSReadLine/ViRegister.cs b/PSReadLine/ViRegister.cs index 48df9965f..b18d27b50 100644 --- a/PSReadLine/ViRegister.cs +++ b/PSReadLine/ViRegister.cs @@ -11,7 +11,32 @@ public partial class PSConsoleReadLine internal sealed class ViRegister { private readonly PSConsoleReadLine _singleton; - private string _text; + private string _localText; + private string Text + { + get + { + if (_singleton.Options.ViClipboardMode == ViClipboardMode.ViRegister) + { + return _localText; + } + else + { + return Internal.Clipboard.GetText(); + } + } + set + { + if (_singleton.Options.ViClipboardMode == ViClipboardMode.ViRegister) + { + _localText = value; + } + else + { + Internal.Clipboard.SetText(value); + } + } + } /// /// Initialize a new instance of the class. @@ -29,7 +54,7 @@ public ViRegister(PSConsoleReadLine singleton) /// Returns whether this register is empty. /// public bool IsEmpty - => String.IsNullOrEmpty(_text); + => String.IsNullOrEmpty(Text); /// /// Returns whether this register contains @@ -41,7 +66,7 @@ public bool IsEmpty /// Gets the raw text contained in the register /// public string RawText - => _text; + => Text; /// /// Records the entire buffer in the register. @@ -67,7 +92,7 @@ public void Record(StringBuilder buffer, int offset, int count) System.Diagnostics.Debug.Assert(offset + count <= buffer.Length); HasLinewiseText = false; - _text = buffer.ToString(offset, count); + Text = buffer.ToString(offset, count); } /// @@ -77,7 +102,7 @@ public void Record(StringBuilder buffer, int offset, int count) public void LinewiseRecord(string text) { HasLinewiseText = true; - _text = text; + Text = text; } public int PasteAfter(StringBuilder buffer, int position) @@ -89,7 +114,7 @@ public int PasteAfter(StringBuilder buffer, int position) if (HasLinewiseText) { - var text = _text; + var text = Text; if (text[0] != '\n') { @@ -127,8 +152,9 @@ public int PasteAfter(StringBuilder buffer, int position) position += 1; } - InsertAt(buffer, _text, position, position); - position += _text.Length - 1; + var text = Text; + InsertAt(buffer, text, position, position); + position += text.Length - 1; return position; } @@ -145,7 +171,7 @@ public int PasteBefore(StringBuilder buffer, int position) position = Math.Max(0, Math.Min(position, buffer.Length - 1)); - var text = _text; + var text = Text; if (text[0] == '\n') { @@ -184,8 +210,9 @@ public int PasteBefore(StringBuilder buffer, int position) } else { - InsertAt(buffer, _text, position, position); - return position + _text.Length - 1; + var text = Text; + InsertAt(buffer, text, position, position); + return position + text.Length - 1; } } @@ -246,7 +273,7 @@ private void RecordPaste(string text, int position, int anchor) #if DEBUG public override string ToString() { - var text = _text.Replace("\n", "\\n"); + var text = Text.Replace("\n", "\\n"); return (HasLinewiseText ? "line: " : "") + "\"" + text + "\""; } #endif From 902e93a2b01b28701580535d0744f1e2048c56ad Mon Sep 17 00:00:00 2001 From: Ryan West Date: Wed, 2 Aug 2023 15:21:27 -0600 Subject: [PATCH 2/2] Add ViRegister Tests for ViClipboardMode Option --- PSReadLine/ViRegister.cs | 21 +++++++++++++----- test/ViRegisterTests.cs | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/PSReadLine/ViRegister.cs b/PSReadLine/ViRegister.cs index b18d27b50..077862946 100644 --- a/PSReadLine/ViRegister.cs +++ b/PSReadLine/ViRegister.cs @@ -12,28 +12,29 @@ internal sealed class ViRegister { private readonly PSConsoleReadLine _singleton; private string _localText; + private PSConsoleReadLineOptions _options; private string Text { get { - if (_singleton.Options.ViClipboardMode == ViClipboardMode.ViRegister) + if (_options?.ViClipboardMode == ViClipboardMode.SystemClipboard) { - return _localText; + return Internal.Clipboard.GetText(); } else { - return Internal.Clipboard.GetText(); + return _localText; } } set { - if (_singleton.Options.ViClipboardMode == ViClipboardMode.ViRegister) + if (_options?.ViClipboardMode == ViClipboardMode.SystemClipboard) { - _localText = value; + Internal.Clipboard.SetText(value); } else { - Internal.Clipboard.SetText(value); + _localText = value; } } } @@ -48,6 +49,14 @@ private string Text public ViRegister(PSConsoleReadLine singleton) { _singleton = singleton; + _options = _singleton?.Options; + } + + internal static ViRegister CreateTestRegister(PSConsoleReadLineOptions options) + { + ViRegister register = new ViRegister(null); + register._options = options; + return register; } /// diff --git a/test/ViRegisterTests.cs b/test/ViRegisterTests.cs index 15c9884da..1eac93311 100644 --- a/test/ViRegisterTests.cs +++ b/test/ViRegisterTests.cs @@ -1,5 +1,6 @@ using System.Text; using Microsoft.PowerShell; +using Microsoft.PowerShell.Internal; using Xunit; namespace Test @@ -152,5 +153,51 @@ public void ViRegister_Lines_LinewisePasteAfter_Lines() Assert.Equal("line1\nline2\nline3\n", buffer.ToString()); Assert.Equal(6, newPosition); } + + [Fact] + public void ViRegister_ClipboardModeViRegister() + { + PSConsoleReadLineOptions options = new PSConsoleReadLineOptions(string.Empty, false) + { + ViClipboardMode = ViClipboardMode.ViRegister + }; + var register = PSConsoleReadLine.ViRegister.CreateTestRegister(options); + + // system under test + + Clipboard.SetText("EmptyClipboard"); + var copyBuffer = new StringBuilder("CopiedText"); + register.Record(copyBuffer); + var pasteBuffer = new StringBuilder(); + register.PasteAfter(pasteBuffer, 0); + + // assert expectations + + Assert.Equal("EmptyClipboard", Clipboard.GetText()); + Assert.Equal("CopiedText", pasteBuffer.ToString()); + } + + [Fact] + public void ViRegister_ClipboardModeSystemClipboard() + { + PSConsoleReadLineOptions options = new PSConsoleReadLineOptions(string.Empty, false) + { + ViClipboardMode = ViClipboardMode.SystemClipboard + }; + var register = PSConsoleReadLine.ViRegister.CreateTestRegister(options); + + // system under test + + Clipboard.SetText("EmptyClipboard"); + var copyBuffer = new StringBuilder("CopiedText"); + register.Record(copyBuffer); + var pasteBuffer = new StringBuilder(); + register.PasteAfter(pasteBuffer, 0); + + // assert expectations + + Assert.Equal("CopiedText", Clipboard.GetText()); + Assert.Equal("CopiedText", pasteBuffer.ToString()); + } } }