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..077862946 100644 --- a/PSReadLine/ViRegister.cs +++ b/PSReadLine/ViRegister.cs @@ -11,7 +11,33 @@ public partial class PSConsoleReadLine internal sealed class ViRegister { private readonly PSConsoleReadLine _singleton; - private string _text; + private string _localText; + private PSConsoleReadLineOptions _options; + private string Text + { + get + { + if (_options?.ViClipboardMode == ViClipboardMode.SystemClipboard) + { + return Internal.Clipboard.GetText(); + } + else + { + return _localText; + } + } + set + { + if (_options?.ViClipboardMode == ViClipboardMode.SystemClipboard) + { + Internal.Clipboard.SetText(value); + } + else + { + _localText = value; + } + } + } /// /// Initialize a new instance of the class. @@ -23,13 +49,21 @@ internal sealed class ViRegister 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; } /// /// Returns whether this register is empty. /// public bool IsEmpty - => String.IsNullOrEmpty(_text); + => String.IsNullOrEmpty(Text); /// /// Returns whether this register contains @@ -41,7 +75,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 +101,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 +111,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 +123,7 @@ public int PasteAfter(StringBuilder buffer, int position) if (HasLinewiseText) { - var text = _text; + var text = Text; if (text[0] != '\n') { @@ -127,8 +161,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 +180,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 +219,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 +282,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 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()); + } } }