Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supports copying to and pasting from the system clipboard via named registers. #2

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions PSReadLine/KeyBindings.vi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal static ConsoleColor AlternateBackground(ConsoleColor bg)
private static Dictionary<PSKeyInfo, KeyHandler> _viChordCTable;
private static Dictionary<PSKeyInfo, KeyHandler> _viChordYTable;
private static Dictionary<PSKeyInfo, KeyHandler> _viChordDGTable;
private static Dictionary<PSKeyInfo, KeyHandler> _viChordDQuoteTable;

private static Dictionary<PSKeyInfo, Dictionary<PSKeyInfo, KeyHandler>> _viCmdChordTable;
private static Dictionary<PSKeyInfo, Dictionary<PSKeyInfo, KeyHandler>> _viInsChordTable;
Expand Down Expand Up @@ -216,6 +217,7 @@ private void SetDefaultViBindings()
{ Keys.Comma, MakeKeyHandler(RepeatLastCharSearchBackwards, "RepeatLastCharSearchBackwards") },
{ Keys.AltH, MakeKeyHandler(ShowParameterHelp, "ShowParameterHelp") },
{ Keys.F1, MakeKeyHandler(ShowCommandHelp, "ShowCommandHelp") },
{ Keys.DQuote, MakeKeyHandler(ViChord, "ChordFirstKey") },
};

// Some bindings are not available on certain platforms
Expand Down Expand Up @@ -301,6 +303,13 @@ private void SetDefaultViBindings()
{ Keys.G, MakeKeyHandler( DeleteRelativeLines, "DeleteRelativeLines") },
};

_viChordDQuoteTable = new Dictionary<PSKeyInfo, KeyHandler>
{
{ Keys.DQuote, MakeKeyHandler( ViSelectNamedRegister, "ViSelectNamedRegister" ) },
{ Keys.Plus, MakeKeyHandler( ViSelectNamedRegister, "ViSelectNamedRegister" ) },
{ Keys.Underbar, MakeKeyHandler( ViSelectNamedRegister, "ViSelectNamedRegister" ) },
};

_viCmdChordTable = new Dictionary<PSKeyInfo, Dictionary<PSKeyInfo, KeyHandler>>();
_viInsChordTable = new Dictionary<PSKeyInfo, Dictionary<PSKeyInfo, KeyHandler>>();

Expand All @@ -310,6 +319,7 @@ private void SetDefaultViBindings()
_viCmdChordTable[Keys.D] = _viChordDTable;
_viCmdChordTable[Keys.C] = _viChordCTable;
_viCmdChordTable[Keys.Y] = _viChordYTable;
_viCmdChordTable[Keys.DQuote] = _viChordDQuoteTable;

_normalCursorSize = _console.CursorSize;
if ((_normalCursorSize < 1) || (_normalCursorSize > 100))
Expand Down
16 changes: 15 additions & 1 deletion PSReadLine/ReadLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,21 @@ void ProcessOneKey(PSKeyInfo key, Dictionary<PSKeyInfo, KeyHandler> dispatchTabl
static PSConsoleReadLine()
{
_singleton = new PSConsoleReadLine();
_viRegister = new ViRegister(_singleton);

_registers = new Dictionary<string, ViRegister> {
[""] = new ViRegister(_singleton, new InMemoryClipboard()),
["_"] = new ViRegister(_singleton, new NoOpClipboard()),
["\""] = new ViRegister(_singleton, new SystemClipboard()),
};

// '+' and '"' are synonyms

_registers["+"] = _registers["\""];

// default register is the unnamed local register

_viRegister = _registers[""];

InitializePropertyInfo();
}

Expand Down
13 changes: 12 additions & 1 deletion PSReadLine/ReadLine.vi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ public static void ViCommandMode(ConsoleKeyInfo? key = null, object arg = null)
_singleton._dispatchTable = _viCmdKeyMap;
_singleton._chordDispatchTable = _viCmdChordTable;
ViBackwardChar();
ViSelectNamedRegister("");
_singleton.ViIndicateCommandMode();
}

Expand Down Expand Up @@ -746,7 +747,7 @@ private static int DeleteLineImpl(int lineIndex, int lineCount)

var deleteText = _singleton._buffer.ToString(range.Offset, range.Count);

_viRegister.LinewiseRecord(deleteText);
_singleton.SaveLinesToClipboard(deleteText);

var deletePosition = range.Offset;
var anchor = _singleton._current;
Expand Down Expand Up @@ -1150,6 +1151,16 @@ private static void ViDGChord(ConsoleKeyInfo? key = null, object arg = null)
ViChordHandler(_viChordDGTable, arg);
}

private static void ViDQuoteChord(ConsoleKeyInfo? key = null, object arg = null)
{
if (!key.HasValue)
{
throw new ArgumentNullException(nameof(key));
}

ViChordHandler(_viChordDQuoteTable, arg);
}

private static bool IsNumeric(PSKeyInfo key)
{
return key.KeyChar >= '0' && key.KeyChar <= '9' && !key.Control && !key.Alt;
Expand Down
99 changes: 81 additions & 18 deletions PSReadLine/ViRegister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,93 @@ namespace Microsoft.PowerShell
{
public partial class PSConsoleReadLine
{
/// <summary>
/// Represents a clipboard that holds a piece of text.
/// </summary>
internal interface IClipboard
{
/// <summary>
/// Retrieves the text stored in the clipboard.
/// </summary>
string GetText();
/// <summary>
/// Stores some text in the clipboard.
/// </summary>
/// <param name="text"></param>
void SetText(string text);
}

/// <summary>
/// Represents an in-memory clipboard.
/// </summary>
internal sealed class InMemoryClipboard : IClipboard
{
private string _text;
public string GetText()
=> _text ?? "";

public void SetText(string text)
=> _text = text;
}

/// <summary>
/// Represents a clipboard that does not store any text.
/// </summary>
internal sealed class NoOpClipboard : IClipboard
{
public string GetText() => "";
public void SetText(string text) { }
}

/// <summary>
/// Represents the system clipboard.
/// </summary>
internal sealed class SystemClipboard : IClipboard
{
public string GetText()
=> Internal.Clipboard.GetText();

public void SetText(string text)
=> Internal.Clipboard.SetText(text);
}

/// <summary>
/// Represents a named register.
/// </summary>
internal sealed class ViRegister
{
private readonly PSConsoleReadLine _singleton;
private string _text;
private readonly IClipboard _clipboard;

/// <summary>
/// Initialize a new instance of the <see cref="ViRegister" /> class.
/// </summary>
/// <param name="singleton">The <see cref="PSConsoleReadLine" /> object.
/// Used to hook into the undo / redo subsystem as part of
/// pasting the contents of the register into a buffer.
/// </param>
public ViRegister(PSConsoleReadLine singleton)
/// <param name="clipboard">The clipboard to store text to and retrieve text from</param>
public ViRegister(PSConsoleReadLine singleton, IClipboard clipboard)
{
_clipboard = clipboard;
_singleton = singleton;
}

/// Initialize a new instance of the <see cref="ViRegister" /> class.
/// </summary>
/// <param name="singleton">The <see cref="PSConsoleReadLine" /> object.
/// Used to hook into the undo / redo subsystem as part of
/// pasting the contents of the register into a buffer.
/// </param>
public ViRegister(PSConsoleReadLine singleton)
: this(singleton, new InMemoryClipboard())
{
}

/// <summary>
/// Returns whether this register is empty.
/// </summary>
public bool IsEmpty
=> String.IsNullOrEmpty(_text);
=> String.IsNullOrEmpty(_clipboard.GetText());

/// <summary>
/// Returns whether this register contains
Expand All @@ -41,16 +103,14 @@ public bool IsEmpty
/// Gets the raw text contained in the register
/// </summary>
public string RawText
=> _text;
=> _clipboard.GetText();

/// <summary>
/// Records the entire buffer in the register.
/// </summary>
/// <param name="buffer"></param>
public void Record(StringBuilder buffer)
{
Record(buffer, 0, buffer.Length);
}
=> Record(buffer, 0, buffer.Length);

/// <summary>
/// Records a piece of text in the register.
Expand All @@ -67,7 +127,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);
_clipboard.SetText(buffer.ToString(offset, count));
}

/// <summary>
Expand All @@ -77,7 +137,7 @@ public void Record(StringBuilder buffer, int offset, int count)
public void LinewiseRecord(string text)
{
HasLinewiseText = true;
_text = text;
_clipboard.SetText(text);
}

public int PasteAfter(StringBuilder buffer, int position)
Expand All @@ -87,9 +147,11 @@ public int PasteAfter(StringBuilder buffer, int position)
return position;
}

var yanked = _clipboard.GetText();

if (HasLinewiseText)
{
var text = _text;
var text = yanked;

if (text[0] != '\n')
{
Expand All @@ -99,7 +161,6 @@ public int PasteAfter(StringBuilder buffer, int position)
// paste text after the next line

var pastePosition = -1;
var newCursorPosition = position;

for (var index = position; index < buffer.Length; index++)
{
Expand Down Expand Up @@ -127,15 +188,17 @@ public int PasteAfter(StringBuilder buffer, int position)
position += 1;
}

InsertAt(buffer, _text, position, position);
position += _text.Length - 1;
InsertAt(buffer, yanked, position, position);
position += yanked.Length - 1;

return position;
}
}

public int PasteBefore(StringBuilder buffer, int position)
{
var yanked = _clipboard.GetText();

if (HasLinewiseText)
{
// currently, in Vi Edit Mode, the cursor may be positioned
Expand All @@ -145,7 +208,7 @@ public int PasteBefore(StringBuilder buffer, int position)

position = Math.Max(0, Math.Min(position, buffer.Length - 1));

var text = _text;
var text = yanked;

if (text[0] == '\n')
{
Expand Down Expand Up @@ -184,8 +247,8 @@ public int PasteBefore(StringBuilder buffer, int position)
}
else
{
InsertAt(buffer, _text, position, position);
return position + _text.Length - 1;
InsertAt(buffer, yanked, position, position);
return position + yanked.Length - 1;
}
}

Expand Down Expand Up @@ -246,7 +309,7 @@ private void RecordPaste(string text, int position, int anchor)
#if DEBUG
public override string ToString()
{
var text = _text.Replace("\n", "\\n");
var text = _clipboard.GetText().Replace("\n", "\\n");
return (HasLinewiseText ? "line: " : "") + "\"" + text + "\"";
}
#endif
Expand Down
Loading