Implement yank-last-arg/yank-nth-arg

Support for yank-last-arg and yank-nth-arg has been added.
Combined with digit-argument, these functions provide a nice way
to copy arguments from previous commands fairly easily.

For example, to copy the 2nd argument from a command 2 entries back,
you can use: Alt+2 Alt+. Alt+.

I also tweaked digit-argument slightly so that when you start with
a negative argument (Alt--) the first digit can override the -1.
In Bash, to enter something like -2, you have to think backwards
and type Alt-2 Alt-- because Alt-- Alt-2 ends up giving you -12.
This commit is contained in:
jason@truewheels.net
2013-12-06 14:03:40 -08:00
parent cf6d4370bd
commit 0ef6b555e3
5 changed files with 171 additions and 8 deletions
+10 -8
View File
@@ -443,14 +443,16 @@ namespace PSConsoleUtilities
// ValidateSet attribute is generated by the script GenerateBuiltinList
[ValidateSet("Abort", "AcceptLine", "AddLine", "BackwardChar", "BackwardDeleteChar",
"BackwardDeleteLine", "BackwardKillLine", "BeginningOfHistory", "BeginningOfLine", "CancelLine",
"ClearScreen", "Complete", "DeleteChar", "DisableDemoMode", "EnableDemoMode",
"EndOfHistory", "EndOfLine", "ExchangePointAndMark", "ForwardChar", "ForwardDeleteLine",
"ForwardSearchHistory", "GotoBrace", "HistorySearchBackward", "HistorySearchForward", "ShellBackwardKillWord",
"KillLine", "ShellKillWord", "NextHistory", "Paste", "PossibleCompletions",
"PreviousHistory", "Redo", "ReverseSearchHistory", "RevertLine", "SetKeyHandler",
"SetMark", "ShellBackwardWord", "ShellForwardWord", "TabCompleteNext", "TabCompletePrevious",
"Undo", "Yank", "YankPop")]
"BackwardDeleteLine", "BackwardKillLine", "BackwardKillWord", "BackwardWord", "BeginningOfHistory",
"BeginningOfLine", "CancelLine", "ClearScreen", "Complete", "DeleteChar",
"DigitArgument", "DisableDemoMode", "EnableDemoMode", "EndOfHistory", "EndOfLine",
"ExchangePointAndMark", "ForwardChar", "ForwardDeleteLine", "ForwardSearchHistory", "ForwardWord",
"GotoBrace", "HistorySearchBackward", "HistorySearchForward", "KillLine", "KillWord",
"NextHistory", "NextWord", "Paste", "PossibleCompletions", "PreviousHistory",
"Redo", "ReverseSearchHistory", "RevertLine", "SelfInsert", "SetKeyHandler",
"SetMark", "ShellBackwardKillWord", "ShellBackwardWord", "ShellForwardWord", "ShellKillWord",
"TabCompleteNext", "TabCompletePrevious", "Undo", "UnixWordRubout", "Yank",
"YankLastArg", "YankNthArg", "YankPop")]
[Parameter(Position = 1, Mandatory = true, ParameterSetName = "Function")]
public string Function { get; set; }
+4
View File
@@ -109,6 +109,7 @@ namespace PSConsoleUtilities
public static ConsoleKeyInfo Equals = new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false);
public static ConsoleKeyInfo CtrlAt = new ConsoleKeyInfo((char)0, ConsoleKey.D2, true, false, true);
public static ConsoleKeyInfo AltUnderbar = new ConsoleKeyInfo('_', ConsoleKey.OemMinus, true, true, false);
public static ConsoleKeyInfo CtrlUnderbar = new ConsoleKeyInfo((char)31, ConsoleKey.OemMinus, true, false, true);
public static ConsoleKeyInfo AltEquals = new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false);
public static ConsoleKeyInfo Space = new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false);
@@ -118,6 +119,7 @@ namespace PSConsoleUtilities
public static ConsoleKeyInfo AltLess = new ConsoleKeyInfo('<', ConsoleKey.OemComma, true, true, false);
public static ConsoleKeyInfo AltGreater = new ConsoleKeyInfo('>', ConsoleKey.OemPeriod, true, true, false);
public static ConsoleKeyInfo CtrlRBracket = new ConsoleKeyInfo((char)29, ConsoleKey.Oem6, false, false, true);
public static ConsoleKeyInfo AltPeriod = new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, true, false);
public static ConsoleKeyInfo Alt0 = new ConsoleKeyInfo('0', ConsoleKey.D0, false, true, false);
public static ConsoleKeyInfo Alt1 = new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false);
@@ -184,6 +186,8 @@ namespace PSConsoleUtilities
public static ConsoleKeyInfo CtrlY = new ConsoleKeyInfo((char)25, ConsoleKey.Y, false, false, true);
public static ConsoleKeyInfo CtrlZ = new ConsoleKeyInfo((char)26, ConsoleKey.Z, false, false, true);
public static ConsoleKeyInfo AltCtrlY = new ConsoleKeyInfo((char)0, ConsoleKey.Y, false, true, true);
public static ConsoleKeyInfo Backspace = new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false);
public static ConsoleKeyInfo CtrlBackspace = new ConsoleKeyInfo((char)0x7f, ConsoleKey.Backspace, false, false, true);
public static ConsoleKeyInfo AltBackspace = new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false);
+18
View File
@@ -537,6 +537,24 @@ namespace PSConsoleUtilities {
}
}
/// <summary>
/// Looks up a localized string similar to Copy the text of the last argument to the input.
/// </summary>
internal static string YankLastArgDescription {
get {
return ResourceManager.GetString("YankLastArgDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Copy the text of the first argument to the input.
/// </summary>
internal static string YankNthArgDescription {
get {
return ResourceManager.GetString("YankNthArgDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Replace the previously yanked text with the text from the next kill ring position.
/// </summary>
+6
View File
@@ -279,4 +279,10 @@
<data name="UnixWordRuboutDescription" xml:space="preserve">
<value>Move the text from the cursor to the start of the current or previous whitespace delimited word to the kill ring</value>
</data>
<data name="YankLastArgDescription" xml:space="preserve">
<value>Copy the text of the last argument to the input</value>
</data>
<data name="YankNthArgDescription" xml:space="preserve">
<value>Copy the text of the first argument to the input</value>
</data>
</root>
+133
View File
@@ -169,6 +169,15 @@ namespace PSConsoleUtilities
private int _killCommandCount;
private int _yankCommandCount;
private int _yankStartPoint;
private int _yankLastArgCommandCount;
class YankLastArgState
{
internal int argument;
internal int historyIndex;
internal int historyIncrement;
internal int startPoint = -1;
}
private YankLastArgState _yankLastArgState;
// Tab completion state
private int _tabCommandCount;
@@ -313,6 +322,7 @@ namespace PSConsoleUtilities
var yankCommandCount = _yankCommandCount;
var tabCommandCount = _tabCommandCount;
var searchHistoryCommandCount = _searchHistoryCommandCount;
var yankLastArgCommandCount = _yankLastArgCommandCount;
var key = ReadKey();
ProcessOneKey(key, _dispatchTable, ignoreIfNoAction: false, arg: null);
@@ -331,6 +341,12 @@ namespace PSConsoleUtilities
// Reset yank command count if it didn't change
_yankCommandCount = 0;
}
if (yankLastArgCommandCount == _yankLastArgCommandCount)
{
// Reset yank last arg command count if it didn't change
_yankLastArgCommandCount = 0;
_yankLastArgState = null;
}
if (tabCommandCount == _tabCommandCount)
{
// Reset tab command count if it didn't change
@@ -514,6 +530,9 @@ namespace PSConsoleUtilities
{ Keys.AltBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
{ Keys.AltEquals, MakeKeyHandler(PossibleCompletions, "PossibleCompletions") },
{ Keys.AltSpace, MakeKeyHandler(SetMark, "SetMark") }, // useless entry here for completeness - brings up system menu on Windows
{ Keys.AltPeriod, MakeKeyHandler(YankLastArg, "YankLastArg") },
{ Keys.AltUnderbar, MakeKeyHandler(YankLastArg, "YankLastArg") },
{ Keys.AltCtrlY, MakeKeyHandler(YankNthArg, "YankNthArg") },
{ Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") },
{ Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") },
{ Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") },
@@ -526,7 +545,10 @@ namespace PSConsoleUtilities
{ Keys.F, MakeKeyHandler(ForwardWord, "ForwardWord") },
{ Keys.R, MakeKeyHandler(RevertLine, "RevertLine") },
{ Keys.Y, MakeKeyHandler(YankPop, "YankPop") },
{ Keys.CtrlY, MakeKeyHandler(YankNthArg, "YankNthArg") },
{ Keys.Backspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
{ Keys.Period, MakeKeyHandler(YankLastArg, "YankLastArg") },
{ Keys.Underbar, MakeKeyHandler(YankLastArg, "YankLastArg") },
};
_emacsCtrlXMap = new Dictionary<ConsoleKeyInfo, KeyHandler>(new ConsoleKeyInfoComparer())
@@ -1657,12 +1679,14 @@ namespace PSConsoleUtilities
return;
}
bool firstKeyAfterNegative = false;
_singleton._statusLinePrompt = "digit-argument: ";
var argBuffer = _singleton._statusBuffer;
argBuffer.Append(key.Value.KeyChar);
if (key.Value.KeyChar == '-')
{
argBuffer.Append('1');
firstKeyAfterNegative = true;
}
_singleton.Render(); // Render prompt
@@ -1682,11 +1706,17 @@ namespace PSConsoleUtilities
{
argBuffer.Insert(0, '-');
}
_singleton.Render(); // Render prompt
continue;
}
if (nextKey.KeyChar >= '0' && nextKey.KeyChar <= '9')
{
if (firstKeyAfterNegative)
{
argBuffer.Remove(1, 1);
firstKeyAfterNegative = false;
}
argBuffer.Append(nextKey.KeyChar);
_singleton.Render(); // Render prompt
continue;
@@ -1907,6 +1937,109 @@ namespace PSConsoleUtilities
_singleton.YankPopImpl();
}
void YankArgImpl(YankLastArgState yankLastArgState)
{
Debug.Assert(yankLastArgState.historyIndex >= 0 && yankLastArgState.historyIndex < _history.Count);
Token[] tokens;
ParseError[] errors;
var buffer = _history[yankLastArgState.historyIndex];
Parser.ParseInput(buffer._line, out tokens, out errors);
int arg = (yankLastArgState.argument < 0)
? tokens.Length + yankLastArgState.argument - 1
: yankLastArgState.argument;
if (arg < 0 || arg >= tokens.Length)
{
Ding();
return;
}
var argText = tokens[arg].Text;
if (yankLastArgState.startPoint < 0)
{
yankLastArgState.startPoint = _current;
Insert(argText);
}
else
{
Replace(yankLastArgState.startPoint, _current - yankLastArgState.startPoint, argText);
}
}
/// <summary>
/// Yank the first argument (after the command) from the previous history line.
/// With an argument, yank the nth argument (starting from 0), if the argument
/// is negative, start from the last argument.
/// </summary>
public static void YankNthArg(ConsoleKeyInfo? key = null, object arg = null)
{
var yankLastArgState = new YankLastArgState
{
argument = (arg is int) ? (int)arg : 1,
historyIndex = _singleton._currentHistoryIndex - 1,
};
_singleton.YankArgImpl(yankLastArgState);
}
/// <summary>
/// Yank the last argument from the previous history line. With an argument,
/// the first time it is invoked, behaves just like YankNthArg. If invoked
/// multiple times, instead it iterates through history and arg sets the direction
/// (negative reverses the direction.)
/// </summary>
public static void YankLastArg(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton._yankLastArgCommandCount += 1;
if (_singleton._yankLastArgCommandCount == 1)
{
_singleton._yankLastArgState = new YankLastArgState
{
argument = (arg is int) ? (int)arg : -1,
historyIncrement = -1,
historyIndex = _singleton._currentHistoryIndex - 1
};
_singleton.YankArgImpl(_singleton._yankLastArgState);
return;
}
var yankLastArgState = _singleton._yankLastArgState;
if (arg != null)
{
if (!(arg is int))
{
Ding();
return;
}
if ((int)arg < 0)
{
yankLastArgState.historyIncrement = -yankLastArgState.historyIncrement;
}
}
yankLastArgState.historyIndex += yankLastArgState.historyIncrement;
// Don't increment more than 1 out of range so it's quick to get back to being in range.
if (yankLastArgState.historyIndex < 0)
{
Ding();
yankLastArgState.historyIndex = 0;
}
else if (yankLastArgState.historyIndex >= _singleton._history.Count)
{
Ding();
yankLastArgState.historyIndex = _singleton._history.Count - 1;
}
else
{
_singleton.YankArgImpl(yankLastArgState);
}
}
#endregion Kill/Yank
private enum FindTokenMode