Support 2 key chords

Support for 2 key chords has been added.

There is a small breaking change in Set-PSReadlineKeyHandler
because the -CtrlX switch is no longer needed.  It could have
been left in, but it's better to just remove it - not very
many people use this module yet.

The signature of key handlers was also changed to better support
features that have not yet been implemented.  The signature change
shouldn't affect any existing key handlers because default arguments
should cover those cases.
This commit is contained in:
Jason Shirk
2013-09-11 17:55:30 -07:00
parent 94b6ad6d4d
commit 3e23733fd5
6 changed files with 210 additions and 136 deletions
+5 -8
View File
@@ -190,13 +190,13 @@ function global:PSConsoleHostReadline
public class SetKeyHandlerCommand : PSCmdlet
{
[Parameter(Position = 0, Mandatory = true)]
[ValidateNotNull]
[ConsoleKeyInfoConverter]
public ConsoleKeyInfo Key { get; set; }
[Alias("Key")]
[ValidateNotNullOrEmpty]
public string[] Chord { get; set; }
[Parameter(Position = 1, Mandatory = true)]
[ValidateNotNull]
public Action Handler { get; set; }
public Action<ConsoleKeyInfo?, object> Handler { get; set; }
[Parameter(Mandatory = true)]
public string BriefDescription { get; set; }
@@ -204,13 +204,10 @@ function global:PSConsoleHostReadline
[Parameter]
public string LongDescription { get; set; }
[Parameter]
public SwitchParameter CtrlXPrefix { get; set; }
[ExcludeFromCodeCoverage]
protected override void EndProcessing()
{
PSConsoleReadLine.SetKeyHandler(Key, CtrlXPrefix.IsPresent, Handler, BriefDescription, LongDescription);
PSConsoleReadLine.SetKeyHandler(Chord, Handler, BriefDescription, LongDescription);
}
}
@@ -2,25 +2,47 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Text;
namespace PSConsoleUtilities
{
// $c = new-object PSConsoleUtilities.ConsoleKeyInfoConverterAttribute
[AttributeUsage(AttributeTargets.Property)]
public class ConsoleKeyInfoConverterAttribute : ArgumentTransformationAttribute
public class ConsoleKeyChordConverter
{
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
/// <summary>
/// Converts a string to a sequence of ConsoleKeyInfo.
/// Sequences are separated by ','. Modifiers
/// appear before the modified key and are separated by '+'.
/// Examples:
/// Ctrl+X
/// Ctrl+D,M
/// Escape,X
/// </summary>
public static ConsoleKeyInfo[] Convert(string chord)
{
if (!(inputData is string))
if (string.IsNullOrEmpty(chord))
{
// pass through
return inputData;
throw new ArgumentNullException("chord");
}
var sequence = (string)inputData;
var tokens = chord.Split(new[] {','});
if (tokens.Length > 2)
{
throw new ArgumentException("Chord can have at most two keys");
}
var result = new ConsoleKeyInfo[tokens.Length];
for (int i = 0; i < tokens.Length; i++)
{
result[i] = ConvertOneSequence(tokens[i]);
}
return result;
}
private static ConsoleKeyInfo ConvertOneSequence(string sequence)
{
Stack<string> tokens = null;
ConsoleModifiers modifiers = 0;
ConsoleKey key = 0;
+2 -2
View File
@@ -49,7 +49,7 @@
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Cmdlets.cs" />
<Compile Include="ConsoleKeyInfoConverterAttribute.cs" />
<Compile Include="ConsoleKeyChordConverter.cs" />
<Compile Include="ConsoleLib.cs" />
<Compile Include="HistoryQueue.cs" />
<Compile Include="Keys.cs" />
@@ -78,4 +78,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+108 -67
View File
@@ -65,11 +65,24 @@ namespace PSConsoleUtilities
class KeyHandler
{
public Action Action;
// Each key handler will be passed 2 arguments. Most will ignore these arguments,
// but having a consistent signature greatly simplifies dispatch. Defaults
// should be included on all handlers that ignore their parameters so they
// can be called from PowerShell without passing anything.
//
// The first arugment is the key that caused the action to be called
// (the second key when it's a 2 key chord). The default is null (it's nullable)
// because PowerShell can't handle default(ConsoleKeyInfo) as a default.
// Most actions will ignore this argument.
//
// The second argument is an arbitrary object. It will usually be either a number
// (e.g. as a repeat count) or a string. Most actions will ignore this argument.
public Action<ConsoleKeyInfo?, object> Action;
public string BriefDescription;
public string LongDescription;
}
static KeyHandler MakeKeyHandler(Action action, string briefDescription, string longDescription = null)
static KeyHandler MakeKeyHandler(Action<ConsoleKeyInfo?, object> action, string briefDescription, string longDescription = null)
{
return new KeyHandler
{
@@ -85,6 +98,7 @@ namespace PSConsoleUtilities
private static readonly Dictionary<ConsoleKeyInfo, KeyHandler> _cmdKeyMap;
private Dictionary<ConsoleKeyInfo, KeyHandler> _dispatchTable;
private Dictionary<ConsoleKeyInfo, Dictionary<ConsoleKeyInfo, KeyHandler>> _chordDispatchTable;
private Dictionary<ConsoleKeyInfo, KeyHandler> _dispatchCtrlXTable;
private Dictionary<ConsoleKeyInfo, KeyHandler> _dispatchMetaTable;
@@ -310,7 +324,7 @@ namespace PSConsoleUtilities
KeyHandler handler;
if (dispatchTable.TryGetValue(key, out handler))
{
handler.Action();
handler.Action(key, 0);
}
else if (!ignoreIfNoAction && key.KeyChar != 0)
{
@@ -380,7 +394,7 @@ namespace PSConsoleUtilities
{ Keys.DownArrow, MakeKeyHandler(NextHistory, "NextHistory") },
{ Keys.Home, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
{ Keys.End, MakeKeyHandler(EndOfLine, "EndOfLine") },
{ Keys.Escape, MakeKeyHandler(EmacsMeta, "EmacsMeta") },
{ Keys.Escape, MakeKeyHandler(Chord, "ChordFirstKey") },
{ Keys.Delete, MakeKeyHandler(DeleteChar, "DeleteChar") },
{ Keys.Tab, MakeKeyHandler(Complete, "Complete") },
{ Keys.CtrlA, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
@@ -393,7 +407,7 @@ namespace PSConsoleUtilities
{ Keys.CtrlK, MakeKeyHandler(KillLine, "KillLine") },
{ Keys.CtrlM, MakeKeyHandler(AcceptLine, "AcceptLine") },
{ Keys.CtrlU, MakeKeyHandler(BackwardKillLine, "BackwardKillLine") },
{ Keys.CtrlX, MakeKeyHandler(EmacsCtrlX, "EmacsCtrlX") },
{ Keys.CtrlX, MakeKeyHandler(Chord, "ChordFirstKey") },
{ Keys.CtrlY, MakeKeyHandler(Yank, "Yank") },
{ Keys.CtrlAt, MakeKeyHandler(SetMark, "SetMark") },
{ Keys.AltB, MakeKeyHandler(EmacsBackwardWord, "EmacsBackwardWord") },
@@ -431,6 +445,7 @@ namespace PSConsoleUtilities
private PSConsoleReadLine()
{
_dispatchTable = new Dictionary<ConsoleKeyInfo, KeyHandler>(_cmdKeyMap);
_chordDispatchTable = new Dictionary<ConsoleKeyInfo, Dictionary<ConsoleKeyInfo, KeyHandler>>();
_buffer = new StringBuilder();
@@ -501,19 +516,21 @@ namespace PSConsoleUtilities
_consoleBuffer = ReadBufferLines(_initialY, 1 + _extraPromptLineCount);
}
private static void EmacsMeta()
private static void Chord(ConsoleKeyInfo? key = null, object arg = null)
{
var key = ReadKey();
_singleton.ProcessOneKey(key, _singleton._dispatchMetaTable, ignoreIfNoAction: true);
if (!key.HasValue)
{
throw new ArgumentNullException("key");
}
Dictionary<ConsoleKeyInfo, KeyHandler> secondKeyDispatchTable;
if (_singleton._chordDispatchTable.TryGetValue(key.Value, out secondKeyDispatchTable))
{
var secondKey = ReadKey();
_singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true);
}
}
private static void EmacsCtrlX()
{
var key = ReadKey();
_singleton.ProcessOneKey(key, _singleton._dispatchCtrlXTable, ignoreIfNoAction: true);
}
private static void Ignore()
private static void Ignore(ConsoleKeyInfo? key = null, object arg = null)
{
}
@@ -527,7 +544,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Reverts all of the input to the current input.
/// </summary>
public static void RevertLine()
public static void RevertLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.RevertLine(supportUndo: true);
}
@@ -536,7 +553,7 @@ namespace PSConsoleUtilities
/// Cancel the current input, leaving the input on the screen,
/// but returns back to the host so the prompt is evaluated again.
/// </summary>
public static void CancelLine()
public static void CancelLine(ConsoleKeyInfo? key = null, object arg = null)
{
// Clear out the input so it doesn't get executed, but
// don't call render so the screen is left alone.
@@ -547,7 +564,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Delete the character before the cursor.
/// </summary>
public static void BackwardDeleteChar()
public static void BackwardDeleteChar(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._buffer.Length > 0 && _singleton._current > 0)
{
@@ -560,7 +577,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Delete the character under the cursor.
/// </summary>
public static void DeleteChar()
public static void DeleteChar(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._buffer.Length > 0 && _singleton._current < _singleton._buffer.Length)
{
@@ -575,7 +592,7 @@ namespace PSConsoleUtilities
/// continuation prompt is displayed on the next line and PSReadline waits for
/// keys to edit the current input.
/// </summary>
public static void AcceptLine()
public static void AcceptLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.ParseInput();
if (_singleton._parseErrors.Any(e => e.IncompleteInput))
@@ -595,7 +612,7 @@ namespace PSConsoleUtilities
/// keys to edit the current input. This is useful to enter multi-line input as
/// a single command even when a single line is complete input by itself.
/// </summary>
public static void AddLine()
public static void AddLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.Insert('\n');
}
@@ -603,7 +620,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Paste text from the system clipboard.
/// </summary>
public static void Paste()
public static void Paste(ConsoleKeyInfo? key = null, object arg = null)
{
if (System.Windows.Clipboard.ContainsText())
{
@@ -620,7 +637,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Move the cursor to the end of the input.
/// </summary>
public static void EndOfLine()
public static void EndOfLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.MoveCursor(_singleton._buffer.Length);
}
@@ -628,7 +645,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Move the cursor to the end of the input.
/// </summary>
public static void BeginningOfLine()
public static void BeginningOfLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.MoveCursor(0);
}
@@ -637,7 +654,7 @@ namespace PSConsoleUtilities
/// Move the cursor one character to the right. This may move the cursor to the next
/// line of multi-line input.
/// </summary>
public static void ForwardChar()
public static void ForwardChar(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._current < _singleton._buffer.Length)
{
@@ -650,7 +667,7 @@ namespace PSConsoleUtilities
/// Move the cursor one character to the left. This may move the cursor to the previous
/// line of multi-line input.
/// </summary>
public static void BackwardChar()
public static void BackwardChar(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._current > 0 && (_singleton._current - 1 < _singleton._buffer.Length))
{
@@ -688,7 +705,7 @@ namespace PSConsoleUtilities
/// Move the cursor forward to the end of the current word, or if between words,
/// to the end of the next word.
/// </summary>
public static void EmacsForwardWord()
public static void EmacsForwardWord(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.ForwardWord(EditMode.Emacs);
}
@@ -696,7 +713,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Move the cursor forward to the start of the next word.
/// </summary>
public static void ForwardWord()
public static void ForwardWord(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.ForwardWord(EditMode.Windows);
}
@@ -713,7 +730,7 @@ namespace PSConsoleUtilities
/// Move the cursor back to the start of the current word, or if between words,
/// the start of the previous word.
/// </summary>
public static void BackwardWord()
public static void BackwardWord(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.BackwardWord(EditMode.Windows);
}
@@ -722,7 +739,7 @@ namespace PSConsoleUtilities
/// Move the cursor back to the start of the current word, or if between words,
/// the start of the previous word.
/// </summary>
public static void EmacsBackwardWord()
public static void EmacsBackwardWord(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.BackwardWord(EditMode.Emacs);
}
@@ -754,7 +771,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Replace the current input with the 'previous' item from PSReadline history.
/// </summary>
public static void PreviousHistory()
public static void PreviousHistory(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._currentHistoryIndex > 0)
{
@@ -766,7 +783,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Replace the current input with the 'next' item from PSReadline history.
/// </summary>
public static void NextHistory()
public static void NextHistory(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._currentHistoryIndex < (_singleton._history.Count - 1))
{
@@ -799,7 +816,7 @@ namespace PSConsoleUtilities
/// Replace the current input with the 'previous' item from PSReadline history
/// that matches the characters between the start and the input and the cursor.
/// </summary>
public static void HistorySearchBackward()
public static void HistorySearchBackward(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.HistorySearch(backward: true);
}
@@ -808,7 +825,7 @@ namespace PSConsoleUtilities
/// Replace the current input with the 'next' item from PSReadline history
/// that matches the characters between the start and the input and the cursor.
/// </summary>
public static void HistorySearchForward()
public static void HistorySearchForward(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.HistorySearch(backward: false);
}
@@ -817,7 +834,7 @@ namespace PSConsoleUtilities
/// Attempt to complete the text surrounding the cursor with the next
/// available completion.
/// </summary>
public static void TabCompleteNext()
public static void TabCompleteNext(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.Complete(forward: true);
}
@@ -826,7 +843,7 @@ namespace PSConsoleUtilities
/// Attempt to complete the text surrounding the cursor with the previous
/// available completion.
/// </summary>
public static void TabCompletePrevious()
public static void TabCompletePrevious(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.Complete(forward: false);
}
@@ -870,7 +887,7 @@ namespace PSConsoleUtilities
/// prefix is used for completion. If trying to complete the longest
/// unambiguous completion, a list of possible completions is displayed.
/// </summary>
public static void Complete()
public static void Complete(ConsoleKeyInfo? key = null, object arg = null)
{
var completions = _singleton.GetCompletions();
if (completions == null || completions.CompletionMatches.Count == 0)
@@ -993,7 +1010,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Display the list of possible completions.
/// </summary>
public static void PossibleCompletions()
public static void PossibleCompletions(ConsoleKeyInfo? key = null, object arg = null)
{
var completions = _singleton.GetCompletions();
if (completions == null || completions.CompletionMatches.Count == 0)
@@ -1063,7 +1080,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Mark the current loction of the cursor for use in a subsequent editing command.
/// </summary>
public static void SetMark()
public static void SetMark(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton._mark = _singleton._current;
}
@@ -1072,7 +1089,7 @@ namespace PSConsoleUtilities
/// The cursor is placed at the location of the mark and the mark is moved
/// to the location of the cursor.
/// </summary>
public static void ExchangePointAndMark()
public static void ExchangePointAndMark(ConsoleKeyInfo? key = null, object arg = null)
{
var tmp = _singleton._mark;
_singleton._mark = _singleton._current;
@@ -1126,7 +1143,7 @@ namespace PSConsoleUtilities
/// Clear the input from the cursor to the end of the input. The cleared text is placed
/// in the kill ring.
/// </summary>
public static void KillLine()
public static void KillLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.Kill(_singleton._current, _singleton._buffer.Length - _singleton._current);
}
@@ -1135,7 +1152,7 @@ namespace PSConsoleUtilities
/// Clear the input from the start of the input to the cursor. The cleared text is placed
/// in the kill ring.
/// </summary>
public static void BackwardKillLine()
public static void BackwardKillLine(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.Kill(0, _singleton._current);
}
@@ -1145,7 +1162,7 @@ namespace PSConsoleUtilities
/// is between words, the input is cleared from the cursor to the end of the next word.
/// The cleared text is placed in the kill ring.
/// </summary>
public static void KillWord()
public static void KillWord(ConsoleKeyInfo? key = null, object arg = null)
{
var token = _singleton.FindToken(_singleton._current, FindTokenMode.CurrentOrNext);
var end = (token.Kind == TokenKind.EndOfInput)
@@ -1159,7 +1176,7 @@ namespace PSConsoleUtilities
/// is between words, the input is cleared from the start of the previous word to the
/// cursor. The cleared text is placed in the kill ring.
/// </summary>
public static void KillBackwardWord()
public static void KillBackwardWord(ConsoleKeyInfo? key = null, object arg = null)
{
var token = _singleton.FindToken(_singleton._current, FindTokenMode.Previous);
var start = token == null
@@ -1184,7 +1201,7 @@ namespace PSConsoleUtilities
/// <summary>
/// Add the most recently killed text to the input.
/// </summary>
public static void Yank()
public static void Yank(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.YankImpl();
}
@@ -1208,7 +1225,7 @@ namespace PSConsoleUtilities
/// If the previous operation was Yank or YankPop, replace the previously yanked
/// text with the next killed text from the kill ring.
/// </summary>
public static void YankPop()
public static void YankPop(ConsoleKeyInfo? key = null, object arg = null)
{
_singleton.YankPopImpl();
}
@@ -1772,12 +1789,19 @@ namespace PSConsoleUtilities
}
if (options._editMode.HasValue)
{
// Switching modes - clear out chord dispatch table
_chordDispatchTable.Clear();
switch (options._editMode)
{
case EditMode.Emacs:
_dispatchTable = new Dictionary<ConsoleKeyInfo, KeyHandler>(_emacsKeyMap);
_dispatchCtrlXTable = new Dictionary<ConsoleKeyInfo, KeyHandler>(_emacsCtrlXMap);
_dispatchMetaTable = new Dictionary<ConsoleKeyInfo, KeyHandler>(_emacsMetaMap);
_chordDispatchTable = new Dictionary<ConsoleKeyInfo, Dictionary<ConsoleKeyInfo, KeyHandler>>();
_chordDispatchTable[Keys.CtrlX] = _dispatchCtrlXTable;
_chordDispatchTable[Keys.Escape] = _dispatchMetaTable;
break;
#if FALSE
case EditMode.Vi:
@@ -1786,6 +1810,7 @@ namespace PSConsoleUtilities
#endif
case EditMode.Windows:
_dispatchTable = new Dictionary<ConsoleKeyInfo, KeyHandler>(_cmdKeyMap);
_chordDispatchTable = new Dictionary<ConsoleKeyInfo, Dictionary<ConsoleKeyInfo, KeyHandler>>();
break;
}
}
@@ -1826,10 +1851,27 @@ namespace PSConsoleUtilities
}
}
private void SetKeyHandlerInternal(ConsoleKeyInfo key, bool ctrlX, Action handler, string briefDescription, string longDescription)
private void SetKeyHandlerInternal(string[] keys, Action<ConsoleKeyInfo?, object> handler, string briefDescription, string longDescription)
{
var table = ctrlX ? _dispatchCtrlXTable : _dispatchTable;
table[key] = MakeKeyHandler(handler, briefDescription, longDescription);
foreach (var key in keys)
{
var chord = ConsoleKeyChordConverter.Convert(key);
if (chord.Length == 1)
{
_dispatchTable[chord[0]] = MakeKeyHandler(handler, briefDescription, longDescription);
}
else
{
_dispatchTable[chord[0]] = MakeKeyHandler(Chord, "ChordFirstKey");
Dictionary<ConsoleKeyInfo, KeyHandler> secondDispatchTable;
if (!_chordDispatchTable.TryGetValue(chord[0], out secondDispatchTable))
{
secondDispatchTable = new Dictionary<ConsoleKeyInfo, KeyHandler>();
_chordDispatchTable[chord[0]] = secondDispatchTable;
}
secondDispatchTable[chord[1]] = MakeKeyHandler(handler, briefDescription, longDescription);
}
}
}
/// <summary>
@@ -1843,9 +1885,9 @@ namespace PSConsoleUtilities
/// <summary>
/// Helper function for the Set-PSReadlineKeyHandler cmdlet.
/// </summary>
public static void SetKeyHandler(ConsoleKeyInfo key, bool ctrlX, Action handler, string briefDescription, string longDescription)
public static void SetKeyHandler(string[] key, Action<ConsoleKeyInfo?, object> handler, string briefDescription, string longDescription)
{
_singleton.SetKeyHandlerInternal(key, ctrlX, handler, briefDescription, longDescription);
_singleton.SetKeyHandlerInternal(key, handler, briefDescription, longDescription);
}
/// <summary>
@@ -1856,29 +1898,28 @@ namespace PSConsoleUtilities
{
foreach (var entry in _singleton._dispatchTable)
{
if (entry.Value.BriefDescription == "Ignore" || entry.Value.BriefDescription == "EmacsMeta")
if (entry.Value.BriefDescription == "Ignore"
|| entry.Value.BriefDescription == "ChordFirstKey")
{
continue;
}
if (entry.Value.BriefDescription == "EmacsCtrlX")
yield return new PSConsoleUtilities.KeyHandler
{
foreach (var xEntry in _singleton._dispatchCtrlXTable)
{
yield return new PSConsoleUtilities.KeyHandler
{
Key = "Ctrl+X," + xEntry.Key.ToGestureString(),
BriefDescription = xEntry.Value.BriefDescription,
LongDescription = xEntry.Value.LongDescription,
};
}
}
else
Key = entry.Key.ToGestureString(),
BriefDescription = entry.Value.BriefDescription,
LongDescription = entry.Value.LongDescription,
};
}
foreach (var entry in _singleton._chordDispatchTable)
{
foreach (var secondEntry in entry.Value)
{
yield return new PSConsoleUtilities.KeyHandler
{
Key = entry.Key.ToGestureString(),
BriefDescription = entry.Value.BriefDescription,
LongDescription = entry.Value.LongDescription,
Key = entry.Key.ToGestureString() + "," + secondEntry.Key.ToGestureString(),
BriefDescription = secondEntry.Value.BriefDescription,
LongDescription = secondEntry.Value.LongDescription,
};
}
}
+2 -2
View File
@@ -103,8 +103,8 @@ namespace TestPSReadLine
HistorySearchCursorMovesToEnd = true,
// ExtraPromptLineCount = 1
});
PSConsoleReadLine.SetKeyHandler(Keys.UpArrow, false, PSConsoleReadLine.HistorySearchBackward, "", "");
PSConsoleReadLine.SetKeyHandler(Keys.DownArrow, false, PSConsoleReadLine.HistorySearchForward, "", "");
PSConsoleReadLine.SetKeyHandler(new[] {"UpArrow"}, PSConsoleReadLine.HistorySearchBackward, "", "");
PSConsoleReadLine.SetKeyHandler(new[] {"DownArrow"}, PSConsoleReadLine.HistorySearchForward, "", "");
while (true)
{
//Console.Write("C:\\Windows\nPS> ");
+61 -47
View File
@@ -104,14 +104,14 @@ namespace UnitTestPSReadLine
class KeyHandler
{
public KeyHandler(ConsoleKeyInfo key, Action handler)
public KeyHandler(string chord, Action<ConsoleKeyInfo?, object> handler)
{
this.Key = key;
this.Chord = chord;
this.Handler = handler;
}
public ConsoleKeyInfo Key { get; private set; }
public Action Handler { get; private set; }
public string Chord { get; private set; }
public Action<ConsoleKeyInfo?, object> Handler { get; private set; }
}
class KeyWithValidation
@@ -478,7 +478,7 @@ namespace UnitTestPSReadLine
foreach (var keyHandler in keyHandlers)
{
PSConsoleReadLine.SetKeyHandler(keyHandler.Key, false, keyHandler.Handler, "", "");
PSConsoleReadLine.SetKeyHandler(new [] {keyHandler.Chord}, keyHandler.Handler, "", "");
}
var colorOptions = new SetPSReadlineOption();
@@ -758,8 +758,8 @@ namespace UnitTestPSReadLine
public void TestSearchHistory()
{
TestSetup(KeyMode.Cmd,
new KeyHandler(_.UpArrow, PSConsoleReadLine.HistorySearchBackward),
new KeyHandler(_.DownArrow, PSConsoleReadLine.HistorySearchForward));
new KeyHandler("UpArrow", PSConsoleReadLine.HistorySearchBackward),
new KeyHandler("DownArrow", PSConsoleReadLine.HistorySearchForward));
// No history
var keys = new [] {_.UpArrow, _.DownArrow, _.Enter};
@@ -1021,11 +1021,15 @@ namespace UnitTestPSReadLine
[TestMethod]
public void TestRevertLine()
{
TestSetup(KeyMode.Cmd);
// Add one test for chords
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+X,Escape", PSConsoleReadLine.RevertLine));
var keys = new [] {_.D, _.I, _.Escape, _.L, _.S, _.Enter};
var result = Test(keys); Assert.AreEqual("ls", result);
keys = new [] {_.D, _.I, _.CtrlX, _.Escape, _.L, _.S, _.Enter};
result = Test(keys); Assert.AreEqual("ls", result);
TestSetup(KeyMode.Emacs);
keys = new [] {_.D, _.I, _.Escape, _.R, _.L, _.S, _.Enter};
result = Test(keys); Assert.AreEqual("ls", result);
@@ -1248,7 +1252,7 @@ namespace UnitTestPSReadLine
public void TestExchangePointAndMark()
{
TestSetup(KeyMode.Emacs,
new KeyHandler(_.CtrlZ, PSConsoleReadLine.ExchangePointAndMark));
new KeyHandler("Ctrl+Z", PSConsoleReadLine.ExchangePointAndMark));
var keys = Keys(_.A, _.B, _.C, _.D, _.E,
new KeyWithValidation(_.CtrlZ, () => AssertCursorLeftIs(0)),
@@ -1417,13 +1421,11 @@ namespace UnitTestPSReadLine
[TestMethod]
public void TestKeyInfoConverterSimpleCharLiteral()
{
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "x");
var result = ConsoleKeyChordConverter.Convert("x");
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(ConsoleKeyInfo));
Assert.AreEqual(result.Length, 1);
var key = (ConsoleKeyInfo) result;
var key = result[0];
Assert.AreEqual(key.KeyChar, 'x');
Assert.AreEqual(key.Key, ConsoleKey.X);
@@ -1433,13 +1435,11 @@ namespace UnitTestPSReadLine
[TestMethod]
public void TestKeyInfoConverterSimpleCharLiteralWithModifiers()
{
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "alt+shift+x");
var result = ConsoleKeyChordConverter.Convert("alt+shift+x");
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(ConsoleKeyInfo));
Assert.AreEqual(result.Length, 1);
var key = (ConsoleKeyInfo) result;
var key = result[0];
Assert.AreEqual(key.KeyChar, 'X');
Assert.AreEqual(key.Key, ConsoleKey.X);
@@ -1449,13 +1449,11 @@ namespace UnitTestPSReadLine
[TestMethod]
public void TestKeyInfoConverterSymbolLiteral()
{
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "}");
var result = ConsoleKeyChordConverter.Convert("}");
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(ConsoleKeyInfo));
Assert.AreEqual(result.Length, 1);
var key = (ConsoleKeyInfo)result;
var key = result[0];
Assert.AreEqual(key.KeyChar, '}');
Assert.AreEqual(key.Key, ConsoleKey.Oem6);
@@ -1466,13 +1464,11 @@ namespace UnitTestPSReadLine
public void TestKeyInfoConverterShiftedSymbolLiteral()
{
// } => shift+] / shift+oem6
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "shift+]");
var result = ConsoleKeyChordConverter.Convert("shift+]");
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(ConsoleKeyInfo));
Assert.AreEqual(result.Length, 1);
var key = (ConsoleKeyInfo)result;
var key = result[0];
Assert.AreEqual(key.KeyChar, '}');
Assert.AreEqual(key.Key, ConsoleKey.Oem6);
@@ -1483,13 +1479,11 @@ namespace UnitTestPSReadLine
public void TestKeyInfoConverterWellKnownConsoleKey()
{
// oem6
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "shift+oem6");
var result = ConsoleKeyChordConverter.Convert("shift+oem6");
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(ConsoleKeyInfo));
Assert.AreEqual(result.Length, 1);
var key = (ConsoleKeyInfo)result;
var key = result[0];
Assert.AreEqual(key.KeyChar, '}');
Assert.AreEqual(key.Key, ConsoleKey.Oem6);
@@ -1497,39 +1491,59 @@ namespace UnitTestPSReadLine
}
[TestMethod]
public void TestKeyInfoConverterPassThrough()
public void TestKeyInfoConverterSequence()
{
// pass through consolekeyinfo
var converter = new ConsoleKeyInfoConverterAttribute();
// oem6
var result = ConsoleKeyChordConverter.Convert("Escape,X");
Assert.IsNotNull(result);
Assert.AreEqual(result.Length, 2);
var key = new ConsoleKeyInfo('x', ConsoleKey.X, true, false, false);
object result = converter.Transform(null, key);
Assert.AreEqual(key, result);
var key = result[0];
Assert.AreEqual(key.KeyChar, (char)27);
Assert.AreEqual(key.Key, ConsoleKey.Escape);
Assert.AreEqual(key.Modifiers, (ConsoleModifiers)0);
key = result[1];
Assert.AreEqual(key.KeyChar, 'x');
Assert.AreEqual(key.Key, ConsoleKey.X);
Assert.AreEqual(key.Modifiers, (ConsoleModifiers)0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void TestKeyInfoConverterInvalidKey()
{
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "escrape");
var result = ConsoleKeyChordConverter.Convert("escrape");
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void TestKeyInfoConverterInvalidModifierTypo()
{
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "alt+shuft+x");
var result = ConsoleKeyChordConverter.Convert("alt+shuft+x");
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void TestKeyInfoConverterInvalidModifierInapplicable()
{
var converter = new ConsoleKeyInfoConverterAttribute();
object result = converter.Transform(null, "shift+}");
var result = ConsoleKeyChordConverter.Convert("shift+}");
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void TestKeyInfoConverterInvalidSubsequence1()
{
var result = ConsoleKeyChordConverter.Convert("x,");
}
[TestMethod]
[ExpectedException(typeof (ArgumentException))]
public void TestKeyInfoConverterInvalidSubsequence2()
{
var result = ConsoleKeyChordConverter.Convert(",x");
}
}
}