Skip to content

Commit

Permalink
Supporting line wise yanks, including paste and undo. (#811)
Browse files Browse the repository at this point in the history
  • Loading branch information
springcomp authored and daxian-dbw committed Nov 8, 2019
1 parent 7c82577 commit f25a089
Show file tree
Hide file tree
Showing 13 changed files with 630 additions and 62 deletions.
15 changes: 5 additions & 10 deletions PSReadLine/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/

using System.Reflection;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("PSReadLine")]
[assembly: AssemblyDescription("Great command line editing in PowerShell")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PSReadLine")]
[assembly: AssemblyCopyright("Copyright")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Make it a friend assembly to 'PSReadLine.Tests' for testing.
[assembly:InternalsVisibleTo("PSReadLine.Tests")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
Expand Down
4 changes: 2 additions & 2 deletions PSReadLine/BasicEditing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ public static void BackwardDeleteLine(ConsoleKeyInfo? key = null, object arg = n
{
if (_singleton._current > 0)
{
_singleton._clipboard = _singleton._buffer.ToString(0, _singleton._current);
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, 0));
_clipboard.Record(_singleton._buffer, 0, _singleton._current);
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, 0));
_singleton._buffer.Remove(0, _singleton._current);
_singleton._current = 0;
_singleton.Render();
Expand Down
72 changes: 72 additions & 0 deletions PSReadLine/MultiLineBufferHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Text;

namespace Microsoft.PowerShell
{
internal static class MultiLineBufferHelper
{
/// <summary>
/// Represents a range of text (subset) of the buffer.
/// </summary>
public class Range
{
public int Offset { get; set; }
public int Count { get; set; }
}

/// <summary>
/// Determines the offset and the length of the fragment
/// in the specified buffer that corresponds to a
/// given number of lines starting from the specified line index
/// </summary>
/// <param name="buffer" />
/// <param name="lineOffset">
/// The 0-based number of the logical line for the current cursor position.
/// This argument comes from a call to the <see cref="PSConsoleReadLine.GetLogicalLineNumber()" />
/// method and is thus guaranteed to represent a valid line number.
/// </param>
/// <param name="lineCount">
/// The number of lines to be taken into account.
/// If more lines are taken into account than there are lines available,
/// this method still returns a valid range corresponding to the available
/// lines from the buffer.
/// </param>
public static Range GetRange(StringBuilder buffer, int lineOffset, int lineCount)
{
var length = buffer.Length;

var startPosition = 0;
var startPositionIdentified = false;

var endPosition = length - 1;

var currentLine = 0;

for (var position = 0; position < length; position++)
{
if (currentLine == lineOffset && !startPositionIdentified)
{
startPosition = position;
startPositionIdentified = true;
}

if (buffer[position] == '\n')
{
currentLine++;
}

if (currentLine == lineOffset + lineCount)
{
endPosition = position;
break;
}
}

return new Range
{
Offset = startPosition,
Count = endPosition - startPosition + 1,
};
}
}
}
3 changes: 0 additions & 3 deletions PSReadLine/PSReadLine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,4 @@
<None Include="..\docs\Set-PSReadLineKeyHandler.md" Link="docs\Set-PSReadLineKeyHandler.md" />
<None Include="..\docs\Set-PSReadLineOption.md" Link="docs\Set-PSReadLineOption.md" />
</ItemGroup>
<ItemGroup Condition=" '$(IsWindows)' != 'true' ">
<Compile Remove="AssemblyInfo.cs" />
</ItemGroup>
</Project>
11 changes: 10 additions & 1 deletion PSReadLine/ReadLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods

private const int EventProcessingRequested = 3;

private static readonly PSConsoleReadLine _singleton = new PSConsoleReadLine();
// *must* be initialized in the static ctor
// because the static member _clipboard depends upon it
// for its own initialization
private static readonly PSConsoleReadLine _singleton;

private static readonly CancellationToken _defaultCancellationToken = new CancellationTokenSource().Token;

Expand Down Expand Up @@ -607,6 +610,12 @@ void ProcessOneKey(PSKeyInfo key, Dictionary<PSKeyInfo, KeyHandler> dispatchTabl
}
}

static PSConsoleReadLine()
{
_singleton = new PSConsoleReadLine();
_clipboard = new ViRegister(_singleton);
}

private PSConsoleReadLine()
{
_mockableMethods = this;
Expand Down
30 changes: 15 additions & 15 deletions PSReadLine/ReadLine.vi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@ public static void DeleteToEnd(ConsoleKeyInfo? key = null, object arg = null)
return;
}

_singleton._clipboard = _singleton._buffer.ToString(_singleton._current, _singleton._buffer.Length - _singleton._current);
_clipboard.Record(_singleton._buffer, _singleton._current, _singleton._buffer.Length - _singleton._current);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
_singleton._current,
DeleteToEnd,
arg
Expand Down Expand Up @@ -258,7 +258,7 @@ private static void DeleteToEndPoint(object arg, int endPoint, Action<ConsoleKey
{
_singleton.SaveToClipboard(_singleton._current, endPoint - _singleton._current);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
_singleton._current,
instigator,
arg
Expand All @@ -277,7 +277,7 @@ private static void DeleteBackwardToEndPoint(object arg, int endPoint, Action<Co

_singleton.SaveToClipboard(endPoint, deleteLength);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
endPoint,
instigator,
arg
Expand All @@ -302,7 +302,7 @@ public static void ViDeleteGlob(ConsoleKeyInfo? key = null, object arg = null)

_singleton.SaveToClipboard(_singleton._current, length);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
_singleton._current,
ViDeleteGlob,
arg
Expand Down Expand Up @@ -334,7 +334,7 @@ public static void DeleteEndOfWord(ConsoleKeyInfo? key = null, object arg = null
}
_singleton.SaveToClipboard(_singleton._current, 1 + endPoint - _singleton._current);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
_singleton._current,
DeleteEndOfWord,
arg
Expand All @@ -361,7 +361,7 @@ public static void ViDeleteEndOfGlob(ConsoleKeyInfo? key = null, object arg = nu

_singleton.SaveToClipboard(_singleton._current, 1 + endPoint - _singleton._current);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
_singleton._current,
ViDeleteEndOfGlob,
arg
Expand Down Expand Up @@ -706,7 +706,7 @@ public static void DeleteLineToFirstChar(ConsoleKeyInfo? key = null, object arg
}

_singleton.SaveToClipboard(i, _singleton._current - i);
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, i, DeleteLineToFirstChar));
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, i, DeleteLineToFirstChar));

_singleton._buffer.Remove(i, _singleton._current - i);
_singleton._current = i;
Expand All @@ -723,8 +723,8 @@ public static void DeleteLineToFirstChar(ConsoleKeyInfo? key = null, object arg
/// </summary>
public static void DeleteLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton._clipboard = _singleton._buffer.ToString();
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, 0));
_clipboard.Record(_singleton._buffer);
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, 0));
_singleton._current = 0;
_singleton._buffer.Remove(0, _singleton._buffer.Length);
_singleton.Render();
Expand All @@ -746,9 +746,9 @@ public static void BackwardDeleteWord(ConsoleKeyInfo? key = null, object arg = n
Ding();
return;
}
_singleton._clipboard = _singleton._buffer.ToString(deletePoint, _singleton._current - deletePoint);
_clipboard.Record(_singleton._buffer, deletePoint, _singleton._current - deletePoint);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
deletePoint,
BackwardDeleteWord,
arg
Expand Down Expand Up @@ -779,9 +779,9 @@ public static void ViBackwardDeleteGlob(ConsoleKeyInfo? key = null, object arg =
Ding();
return;
}
_singleton._clipboard = _singleton._buffer.ToString(deletePoint, _singleton._current - deletePoint);
_clipboard.Record(_singleton._buffer, deletePoint, _singleton._current - deletePoint);
_singleton.SaveEditItem(EditItemDelete.Create(
_singleton._clipboard,
_clipboard,
deletePoint,
BackwardDeleteWord,
arg
Expand Down Expand Up @@ -823,7 +823,7 @@ private static void DeleteRange(int first, int last, Action<ConsoleKeyInfo?, obj
int length = last - first + 1;

_singleton.SaveToClipboard(first, length);
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, first, action));
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, first, action));
_singleton._current = first;
_singleton._buffer.Remove(first, length);
_singleton.Render();
Expand Down
42 changes: 35 additions & 7 deletions PSReadLine/UndoRedo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,18 @@ class EditItemInsertString : EditItem
{
// The string inserted tells us the length to delete on undo.
// The contents of the string are only needed for redo.
private string _insertedString;
private int _insertStartPosition;
private readonly string _insertedString;
private readonly int _insertStartPosition;

protected EditItemInsertString(string str, int position)
{
_insertedString = str;
_insertStartPosition = position;
}

public static EditItem Create(string str, int position)
{
return new EditItemInsertString
{
_insertedString = str,
_insertStartPosition = position
};
return new EditItemInsertString(str, position);
}

public override void Undo()
Expand All @@ -175,6 +177,32 @@ public override void Redo()
}
}

[DebuggerDisplay("Insert '{_insertedString}' ({_insertStartPosition}, Anchor: {_insertAnchor})")]
class EditItemInsertLines : EditItemInsertString
{
// in linewise pastes, the _insertAnchor represents the position
// of the cursor at the time paste was invoked. This is recorded
// so as to be restored when undoing the paste.
private readonly int _insertAnchor;

private EditItemInsertLines(string str, int position, int anchor)
:base(str, position)
{
_insertAnchor = anchor;
}

public static EditItem Create(string str, int position, int anchor)
{
return new EditItemInsertLines(str, position, anchor);
}

public override void Undo()
{
base.Undo();
_singleton._current = _insertAnchor;
}
}

[DebuggerDisplay("Delete '{_deletedString}' ({_deleteStartPosition})")]
class EditItemDelete : EditItem
{
Expand Down
Loading

0 comments on commit f25a089

Please sign in to comment.