Skip to content

Commit f25a089

Browse files
springcompdaxian-dbw
authored andcommitted
Supporting line wise yanks, including paste and undo. (#811)
1 parent 7c82577 commit f25a089

13 files changed

+630
-62
lines changed

PSReadLine/AssemblyInfo.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,16 @@
22
Copyright (c) Microsoft Corporation. All rights reserved.
33
--********************************************************************/
44

5-
using System.Reflection;
6-
using System.Runtime.InteropServices;
75
using System.Diagnostics.CodeAnalysis;
6+
using System.Runtime.InteropServices;
7+
using System.Runtime.CompilerServices;
88

99
// General Information about an assembly is controlled through the following
1010
// set of attributes. Change these attribute values to modify the information
1111
// associated with an assembly.
12-
[assembly: AssemblyTitle("PSReadLine")]
13-
[assembly: AssemblyDescription("Great command line editing in PowerShell")]
14-
[assembly: AssemblyConfiguration("")]
15-
[assembly: AssemblyCompany("")]
16-
[assembly: AssemblyProduct("PSReadLine")]
17-
[assembly: AssemblyCopyright("Copyright")]
18-
[assembly: AssemblyTrademark("")]
19-
[assembly: AssemblyCulture("")]
12+
13+
// Make it a friend assembly to 'PSReadLine.Tests' for testing.
14+
[assembly:InternalsVisibleTo("PSReadLine.Tests")]
2015

2116
// Setting ComVisible to false makes the types in this assembly not visible
2217
// to COM components. If you need to access a type in this assembly from

PSReadLine/BasicEditing.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ public static void BackwardDeleteLine(ConsoleKeyInfo? key = null, object arg = n
121121
{
122122
if (_singleton._current > 0)
123123
{
124-
_singleton._clipboard = _singleton._buffer.ToString(0, _singleton._current);
125-
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, 0));
124+
_clipboard.Record(_singleton._buffer, 0, _singleton._current);
125+
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, 0));
126126
_singleton._buffer.Remove(0, _singleton._current);
127127
_singleton._current = 0;
128128
_singleton.Render();

PSReadLine/MultiLineBufferHelper.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Text;
3+
4+
namespace Microsoft.PowerShell
5+
{
6+
internal static class MultiLineBufferHelper
7+
{
8+
/// <summary>
9+
/// Represents a range of text (subset) of the buffer.
10+
/// </summary>
11+
public class Range
12+
{
13+
public int Offset { get; set; }
14+
public int Count { get; set; }
15+
}
16+
17+
/// <summary>
18+
/// Determines the offset and the length of the fragment
19+
/// in the specified buffer that corresponds to a
20+
/// given number of lines starting from the specified line index
21+
/// </summary>
22+
/// <param name="buffer" />
23+
/// <param name="lineOffset">
24+
/// The 0-based number of the logical line for the current cursor position.
25+
/// This argument comes from a call to the <see cref="PSConsoleReadLine.GetLogicalLineNumber()" />
26+
/// method and is thus guaranteed to represent a valid line number.
27+
/// </param>
28+
/// <param name="lineCount">
29+
/// The number of lines to be taken into account.
30+
/// If more lines are taken into account than there are lines available,
31+
/// this method still returns a valid range corresponding to the available
32+
/// lines from the buffer.
33+
/// </param>
34+
public static Range GetRange(StringBuilder buffer, int lineOffset, int lineCount)
35+
{
36+
var length = buffer.Length;
37+
38+
var startPosition = 0;
39+
var startPositionIdentified = false;
40+
41+
var endPosition = length - 1;
42+
43+
var currentLine = 0;
44+
45+
for (var position = 0; position < length; position++)
46+
{
47+
if (currentLine == lineOffset && !startPositionIdentified)
48+
{
49+
startPosition = position;
50+
startPositionIdentified = true;
51+
}
52+
53+
if (buffer[position] == '\n')
54+
{
55+
currentLine++;
56+
}
57+
58+
if (currentLine == lineOffset + lineCount)
59+
{
60+
endPosition = position;
61+
break;
62+
}
63+
}
64+
65+
return new Range
66+
{
67+
Offset = startPosition,
68+
Count = endPosition - startPosition + 1,
69+
};
70+
}
71+
}
72+
}

PSReadLine/PSReadLine.csproj

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,4 @@
3232
<None Include="..\docs\Set-PSReadLineKeyHandler.md" Link="docs\Set-PSReadLineKeyHandler.md" />
3333
<None Include="..\docs\Set-PSReadLineOption.md" Link="docs\Set-PSReadLineOption.md" />
3434
</ItemGroup>
35-
<ItemGroup Condition=" '$(IsWindows)' != 'true' ">
36-
<Compile Remove="AssemblyInfo.cs" />
37-
</ItemGroup>
3835
</Project>

PSReadLine/ReadLine.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods
3737

3838
private const int EventProcessingRequested = 3;
3939

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

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

@@ -607,6 +610,12 @@ void ProcessOneKey(PSKeyInfo key, Dictionary<PSKeyInfo, KeyHandler> dispatchTabl
607610
}
608611
}
609612

613+
static PSConsoleReadLine()
614+
{
615+
_singleton = new PSConsoleReadLine();
616+
_clipboard = new ViRegister(_singleton);
617+
}
618+
610619
private PSConsoleReadLine()
611620
{
612621
_mockableMethods = this;

PSReadLine/ReadLine.vi.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,9 @@ public static void DeleteToEnd(ConsoleKeyInfo? key = null, object arg = null)
221221
return;
222222
}
223223

224-
_singleton._clipboard = _singleton._buffer.ToString(_singleton._current, _singleton._buffer.Length - _singleton._current);
224+
_clipboard.Record(_singleton._buffer, _singleton._current, _singleton._buffer.Length - _singleton._current);
225225
_singleton.SaveEditItem(EditItemDelete.Create(
226-
_singleton._clipboard,
226+
_clipboard,
227227
_singleton._current,
228228
DeleteToEnd,
229229
arg
@@ -258,7 +258,7 @@ private static void DeleteToEndPoint(object arg, int endPoint, Action<ConsoleKey
258258
{
259259
_singleton.SaveToClipboard(_singleton._current, endPoint - _singleton._current);
260260
_singleton.SaveEditItem(EditItemDelete.Create(
261-
_singleton._clipboard,
261+
_clipboard,
262262
_singleton._current,
263263
instigator,
264264
arg
@@ -277,7 +277,7 @@ private static void DeleteBackwardToEndPoint(object arg, int endPoint, Action<Co
277277

278278
_singleton.SaveToClipboard(endPoint, deleteLength);
279279
_singleton.SaveEditItem(EditItemDelete.Create(
280-
_singleton._clipboard,
280+
_clipboard,
281281
endPoint,
282282
instigator,
283283
arg
@@ -302,7 +302,7 @@ public static void ViDeleteGlob(ConsoleKeyInfo? key = null, object arg = null)
302302

303303
_singleton.SaveToClipboard(_singleton._current, length);
304304
_singleton.SaveEditItem(EditItemDelete.Create(
305-
_singleton._clipboard,
305+
_clipboard,
306306
_singleton._current,
307307
ViDeleteGlob,
308308
arg
@@ -334,7 +334,7 @@ public static void DeleteEndOfWord(ConsoleKeyInfo? key = null, object arg = null
334334
}
335335
_singleton.SaveToClipboard(_singleton._current, 1 + endPoint - _singleton._current);
336336
_singleton.SaveEditItem(EditItemDelete.Create(
337-
_singleton._clipboard,
337+
_clipboard,
338338
_singleton._current,
339339
DeleteEndOfWord,
340340
arg
@@ -361,7 +361,7 @@ public static void ViDeleteEndOfGlob(ConsoleKeyInfo? key = null, object arg = nu
361361

362362
_singleton.SaveToClipboard(_singleton._current, 1 + endPoint - _singleton._current);
363363
_singleton.SaveEditItem(EditItemDelete.Create(
364-
_singleton._clipboard,
364+
_clipboard,
365365
_singleton._current,
366366
ViDeleteEndOfGlob,
367367
arg
@@ -706,7 +706,7 @@ public static void DeleteLineToFirstChar(ConsoleKeyInfo? key = null, object arg
706706
}
707707

708708
_singleton.SaveToClipboard(i, _singleton._current - i);
709-
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, i, DeleteLineToFirstChar));
709+
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, i, DeleteLineToFirstChar));
710710

711711
_singleton._buffer.Remove(i, _singleton._current - i);
712712
_singleton._current = i;
@@ -723,8 +723,8 @@ public static void DeleteLineToFirstChar(ConsoleKeyInfo? key = null, object arg
723723
/// </summary>
724724
public static void DeleteLine(ConsoleKeyInfo? key = null, object arg = null)
725725
{
726-
_singleton._clipboard = _singleton._buffer.ToString();
727-
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, 0));
726+
_clipboard.Record(_singleton._buffer);
727+
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, 0));
728728
_singleton._current = 0;
729729
_singleton._buffer.Remove(0, _singleton._buffer.Length);
730730
_singleton.Render();
@@ -746,9 +746,9 @@ public static void BackwardDeleteWord(ConsoleKeyInfo? key = null, object arg = n
746746
Ding();
747747
return;
748748
}
749-
_singleton._clipboard = _singleton._buffer.ToString(deletePoint, _singleton._current - deletePoint);
749+
_clipboard.Record(_singleton._buffer, deletePoint, _singleton._current - deletePoint);
750750
_singleton.SaveEditItem(EditItemDelete.Create(
751-
_singleton._clipboard,
751+
_clipboard,
752752
deletePoint,
753753
BackwardDeleteWord,
754754
arg
@@ -779,9 +779,9 @@ public static void ViBackwardDeleteGlob(ConsoleKeyInfo? key = null, object arg =
779779
Ding();
780780
return;
781781
}
782-
_singleton._clipboard = _singleton._buffer.ToString(deletePoint, _singleton._current - deletePoint);
782+
_clipboard.Record(_singleton._buffer, deletePoint, _singleton._current - deletePoint);
783783
_singleton.SaveEditItem(EditItemDelete.Create(
784-
_singleton._clipboard,
784+
_clipboard,
785785
deletePoint,
786786
BackwardDeleteWord,
787787
arg
@@ -823,7 +823,7 @@ private static void DeleteRange(int first, int last, Action<ConsoleKeyInfo?, obj
823823
int length = last - first + 1;
824824

825825
_singleton.SaveToClipboard(first, length);
826-
_singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, first, action));
826+
_singleton.SaveEditItem(EditItemDelete.Create(_clipboard, first, action));
827827
_singleton._current = first;
828828
_singleton._buffer.Remove(first, length);
829829
_singleton.Render();

PSReadLine/UndoRedo.cs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,18 @@ class EditItemInsertString : EditItem
148148
{
149149
// The string inserted tells us the length to delete on undo.
150150
// The contents of the string are only needed for redo.
151-
private string _insertedString;
152-
private int _insertStartPosition;
151+
private readonly string _insertedString;
152+
private readonly int _insertStartPosition;
153+
154+
protected EditItemInsertString(string str, int position)
155+
{
156+
_insertedString = str;
157+
_insertStartPosition = position;
158+
}
153159

154160
public static EditItem Create(string str, int position)
155161
{
156-
return new EditItemInsertString
157-
{
158-
_insertedString = str,
159-
_insertStartPosition = position
160-
};
162+
return new EditItemInsertString(str, position);
161163
}
162164

163165
public override void Undo()
@@ -175,6 +177,32 @@ public override void Redo()
175177
}
176178
}
177179

180+
[DebuggerDisplay("Insert '{_insertedString}' ({_insertStartPosition}, Anchor: {_insertAnchor})")]
181+
class EditItemInsertLines : EditItemInsertString
182+
{
183+
// in linewise pastes, the _insertAnchor represents the position
184+
// of the cursor at the time paste was invoked. This is recorded
185+
// so as to be restored when undoing the paste.
186+
private readonly int _insertAnchor;
187+
188+
private EditItemInsertLines(string str, int position, int anchor)
189+
:base(str, position)
190+
{
191+
_insertAnchor = anchor;
192+
}
193+
194+
public static EditItem Create(string str, int position, int anchor)
195+
{
196+
return new EditItemInsertLines(str, position, anchor);
197+
}
198+
199+
public override void Undo()
200+
{
201+
base.Undo();
202+
_singleton._current = _insertAnchor;
203+
}
204+
}
205+
178206
[DebuggerDisplay("Delete '{_deletedString}' ({_deleteStartPosition})")]
179207
class EditItemDelete : EditItem
180208
{

0 commit comments

Comments
 (0)