private bool IsDoneWithCompletions(CompletionResult currentCompletion, PSKeyInfo nextKey) { return(nextKey == Keys.Space || nextKey == Keys.Enter || KeysEndingCompletion.TryGetValue(currentCompletion.ResultType, out var doneKeys) && doneKeys.Contains(nextKey)); }
private void ReadOneOrMoreKeys() { _readkeyStopwatch.Restart(); while (_console.KeyAvailable) { // _charMap is only guaranteed to accumulate input while KeyAvailable // returns false. Make sure to check KeyAvailable after every ProcessKey call, // and clear it in a loop in case the input was something like ^[[1 which can // be 3, 2, or part of 1 key depending on timing. _charMap.ProcessKey(_console.ReadKey()); while (_charMap.KeyAvailable) { var key = PSKeyInfo.FromConsoleKeyInfo(_charMap.ReadKey()); _lastNKeys.Enqueue(key); _queuedKeys.Enqueue(key); } if (_readkeyStopwatch.ElapsedMilliseconds > 2) { // Don't spend too long in this loop if there are lots of queued keys break; } } if (_queuedKeys.Count == 0) { while (!_charMap.KeyAvailable) { // Don't want to block when there is an escape sequence being read. if (_charMap.InEscapeSequence) { if (_console.KeyAvailable) { _charMap.ProcessKey(_console.ReadKey()); } else { // We don't want to sleep for the whole escape timeout // or the user will have a laggy console, but there's // nothing to block on at this point either, so do a // small sleep to yield the CPU while we're waiting // to decide what the input was. This will only run // if there are no keys waiting to be read. Thread.Sleep(5); } } else { _charMap.ProcessKey(_console.ReadKey()); } } while (_charMap.KeyAvailable) { var key = PSKeyInfo.FromConsoleKeyInfo(_charMap.ReadKey()); _lastNKeys.Enqueue(key); _queuedKeys.Enqueue(key); } } }
private static void Chord(ConsoleKeyInfo?key = null, object arg = null) { if (!key.HasValue) { throw new ArgumentNullException(nameof(key)); } if (_singleton._chordDispatchTable.TryGetValue(PSKeyInfo.FromConsoleKeyInfo(key.Value), out var secondKeyDispatchTable)) { var secondKey = ReadKey(); _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: arg); } }
private void PrependQueuedKeys(PSKeyInfo key) { if (_queuedKeys.Count > 0) { // This should almost never happen so being inefficient is fine. var list = new List <PSKeyInfo>(_queuedKeys); _queuedKeys.Clear(); _queuedKeys.Enqueue(key); list.ForEach(k => _queuedKeys.Enqueue(k)); } else { _queuedKeys.Enqueue(key); } }
/// <summary> /// Chords in vi needs special handling because a numeric argument can be input between the 1st and 2nd key. /// </summary> private static void ViChord(ConsoleKeyInfo?key = null, object arg = null) { if (!key.HasValue) { throw new ArgumentNullException(nameof(key)); } if (arg != null) { Chord(key, arg); return; } if (_singleton._chordDispatchTable.TryGetValue(PSKeyInfo.FromConsoleKeyInfo(key.Value), out var secondKeyDispatchTable)) { var secondKey = ReadKey(); if (secondKeyDispatchTable.TryGetValue(secondKey, out var handler)) { _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: arg); } else if (!IsNumeric(secondKey)) { _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: arg); } else { var argBuffer = _singleton._statusBuffer; argBuffer.Clear(); _singleton._statusLinePrompt = "digit-argument: "; while (IsNumeric(secondKey)) { argBuffer.Append(secondKey.KeyChar); _singleton.Render(); secondKey = ReadKey(); } int numericArg = int.Parse(argBuffer.ToString()); if (secondKeyDispatchTable.TryGetValue(secondKey, out handler)) { _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: numericArg); } else { Ding(); } argBuffer.Clear(); _singleton.ClearStatusMessage(render: true); } } }
/// <summary> /// Chords in vi needs special handling because a numeric argument can be input between the 1st and 2nd key. /// </summary> private static void ViChord(ConsoleKeyInfo?key = null, object arg = null) { if (!key.HasValue) { throw new ArgumentNullException(nameof(key)); } if (arg != null) { Chord(key, arg); return; } if (_singleton._chordDispatchTable.TryGetValue(PSKeyInfo.FromConsoleKeyInfo(key.Value), out var secondKeyDispatchTable)) { ViChordHandler(secondKeyDispatchTable, arg); } }
void ProcessOneKey(PSKeyInfo key, Dictionary <PSKeyInfo, KeyHandler> dispatchTable, bool ignoreIfNoAction, object arg) { var consoleKey = key.AsConsoleKeyInfo(); // Our dispatch tables are built as much as possible in a portable way, so for example, // we avoid depending on scan codes like ConsoleKey.Oem6 and instead look at the // PSKeyInfo.Key. We also want to ignore the shift state as that may differ on // different keyboard layouts. // // That said, we first look up exactly what we get from Console.ReadKey - that will fail // most of the time, and when it does, we normalize the key. if (!dispatchTable.TryGetValue(key, out var handler)) { // If we see a control character where Ctrl wasn't used but shift was, treat that like // shift hadn't be pressed. This cleanly allows Shift+Backspace without adding a key binding. if (key.Shift && !key.Control && !key.Alt) { var c = consoleKey.KeyChar; if (c != '\0' && char.IsControl(c)) { key = PSKeyInfo.From(consoleKey.Key); dispatchTable.TryGetValue(key, out handler); } } } if (handler != null) { if (handler.ScriptBlock != null) { CallPossibleExternalApplication(() => handler.Action(consoleKey, arg)); } else { handler.Action(consoleKey, arg); } } else if (!ignoreIfNoAction) { SelfInsert(consoleKey, arg); } }
private void SetKeyHandlerInternal(string[] keys, Action <ConsoleKeyInfo?, object> handler, string briefDescription, string longDescription, ScriptBlock scriptBlock) { foreach (var key in keys) { var chord = ConsoleKeyChordConverter.Convert(key); var firstKey = PSKeyInfo.FromConsoleKeyInfo(chord[0]); if (chord.Length == 1) { _dispatchTable[firstKey] = MakeKeyHandler(handler, briefDescription, longDescription, scriptBlock); } else { _dispatchTable[firstKey] = MakeKeyHandler(Chord, "ChordFirstKey"); if (!_chordDispatchTable.TryGetValue(firstKey, out var secondDispatchTable)) { secondDispatchTable = new Dictionary <PSKeyInfo, KeyHandler>(); _chordDispatchTable[firstKey] = secondDispatchTable; } secondDispatchTable[PSKeyInfo.FromConsoleKeyInfo(chord[1])] = MakeKeyHandler(handler, briefDescription, longDescription, scriptBlock); } } }
private void RemoveKeyHandlerInternal(string[] keys) { foreach (var key in keys) { var chord = ConsoleKeyChordConverter.Convert(key); var firstKey = PSKeyInfo.FromConsoleKeyInfo(chord[0]); if (chord.Length == 1) { _dispatchTable.Remove(firstKey); } else { if (_chordDispatchTable.TryGetValue(firstKey, out var secondDispatchTable)) { secondDispatchTable.Remove(PSKeyInfo.FromConsoleKeyInfo(chord[1])); if (secondDispatchTable.Count == 0) { _dispatchTable.Remove(firstKey); } } } } }
static PSKeyInfo Key(ConsoleKey key) => PSKeyInfo.From(key);
static PSKeyInfo Alt(char c) => PSKeyInfo.WithAlt(c);
static PSKeyInfo Alt(ConsoleKey key) => PSKeyInfo.WithAlt(key);
private static bool IsNumeric(PSKeyInfo key) { return(key.KeyChar >= '0' && key.KeyChar <= '9' && !key.Control && !key.Alt); }
static PSKeyInfo Ctrl(ConsoleKey key) => PSKeyInfo.WithCtrl(key);
/// <summary> /// Return key handlers bound to specified chords. /// </summary> /// <returns></returns> public static IEnumerable <PowerShell.KeyHandler> GetKeyHandlers(string[] Chord) { var boundFunctions = new HashSet <string>(StringComparer.OrdinalIgnoreCase); if (Chord == null || Chord.Length == 0) { yield break; } foreach (string Key in Chord) { ConsoleKeyInfo[] consoleKeyChord = ConsoleKeyChordConverter.Convert(Key); PSKeyInfo firstKey = PSKeyInfo.FromConsoleKeyInfo(consoleKeyChord[0]); if (_singleton._dispatchTable.TryGetValue(firstKey, out KeyHandler entry)) { if (consoleKeyChord.Length == 1) { yield return(new PowerShell.KeyHandler { Key = firstKey.KeyStr, Function = entry.BriefDescription, Description = entry.LongDescription, Group = GetDisplayGrouping(entry.BriefDescription), }); } else { PSKeyInfo secondKey = PSKeyInfo.FromConsoleKeyInfo(consoleKeyChord[1]); if (_singleton._chordDispatchTable.TryGetValue(firstKey, out var secondDispatchTable) && secondDispatchTable.TryGetValue(secondKey, out entry)) { yield return(new PowerShell.KeyHandler { Key = firstKey.KeyStr + "," + secondKey.KeyStr, Function = entry.BriefDescription, Description = entry.LongDescription, Group = GetDisplayGrouping(entry.BriefDescription), }); } } } // If in Vi mode, also check Vi's command mode list. if (_singleton._options.EditMode == EditMode.Vi) { if (_viCmdKeyMap.TryGetValue(firstKey, out entry)) { if (consoleKeyChord.Length == 1) { if (entry.BriefDescription == "Ignore") { continue; } yield return(new PowerShell.KeyHandler { Key = "<" + firstKey.KeyStr + ">", Function = entry.BriefDescription, Description = entry.LongDescription, Group = GetDisplayGrouping(entry.BriefDescription), }); } else { PSKeyInfo secondKey = PSKeyInfo.FromConsoleKeyInfo(consoleKeyChord[1]); if (_viCmdChordTable.TryGetValue(firstKey, out var secondDispatchTable) && secondDispatchTable.TryGetValue(secondKey, out entry)) { if (entry.BriefDescription == "Ignore") { continue; } yield return(new PowerShell.KeyHandler { Key = "<" + firstKey.KeyStr + "," + secondKey.KeyStr + ">", Function = entry.BriefDescription, Description = entry.LongDescription, Group = GetDisplayGrouping(entry.BriefDescription), }); } } } } } }
static PSKeyInfo Key(char c) => PSKeyInfo.From(c);
static PSKeyInfo CtrlAlt(char c) => PSKeyInfo.WithCtrlAlt(c);
static PSKeyInfo CtrlShift(ConsoleKey key) => PSKeyInfo.WithShiftCtrl(key);
static PSKeyInfo Shift(ConsoleKey key) => PSKeyInfo.WithShift(key);