/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; using System.Reflection; using System.Threading; using Microsoft.PowerShell.Internal; namespace Microsoft.PowerShell { public partial class PSConsoleReadLine { private readonly PSConsoleReadlineOptions _options; private PSConsoleReadlineOptions Options { get { return _options; } } private void SetOptionsInternal(SetPSReadlineOption options) { if (options.ContinuationPrompt != null) { Options.ContinuationPrompt = options.ContinuationPrompt; } if (options._continuationPromptForegroundColor.HasValue) { Options.ContinuationPromptForegroundColor = options.ContinuationPromptForegroundColor; } if (options._continuationPromptBackgroundColor.HasValue) { Options.ContinuationPromptBackgroundColor = options.ContinuationPromptBackgroundColor; } if (options._emphasisBackgroundColor.HasValue) { Options.EmphasisBackgroundColor = options.EmphasisBackgroundColor; } if (options._emphasisForegroundColor.HasValue) { Options.EmphasisForegroundColor = options.EmphasisForegroundColor; } if (options._errorBackgroundColor.HasValue) { Options.ErrorBackgroundColor = options.ErrorBackgroundColor; } if (options._errorForegroundColor.HasValue) { Options.ErrorForegroundColor = options.ErrorForegroundColor; } if (options._historyNoDuplicates.HasValue) { Options.HistoryNoDuplicates = options.HistoryNoDuplicates; } if (options._historySearchCursorMovesToEnd.HasValue) { Options.HistorySearchCursorMovesToEnd = options.HistorySearchCursorMovesToEnd; } if (options._addToHistoryHandlerSpecified) { Options.AddToHistoryHandler = options.AddToHistoryHandler; } if (options._commandValidationHandlerSpecified) { Options.CommandValidationHandler = options.CommandValidationHandler; } if (options._maximumHistoryCount.HasValue) { Options.MaximumHistoryCount = options.MaximumHistoryCount; if (_history != null) { var newHistory = new HistoryQueue(Options.MaximumHistoryCount); while (_history.Count > Options.MaximumHistoryCount) { _history.Dequeue(); } while (_history.Count > 0) { newHistory.Enqueue(_history.Dequeue()); } _history = newHistory; _currentHistoryIndex = _history.Count; } } if (options._maximumKillRingCount.HasValue) { Options.MaximumKillRingCount = options.MaximumKillRingCount; // TODO - make _killRing smaller } if (options._editMode.HasValue) { Options.EditMode = options.EditMode; // Switching/resetting modes - clear out chord dispatch table _chordDispatchTable.Clear(); SetDefaultBindings(Options.EditMode); } if (options._showToolTips.HasValue) { Options.ShowToolTips = options.ShowToolTips; } if (options._extraPromptLineCount.HasValue) { Options.ExtraPromptLineCount = options.ExtraPromptLineCount; } if (options._dingTone.HasValue) { Options.DingTone = options.DingTone; } if (options._dingDuration.HasValue) { Options.DingDuration = options.DingDuration; } if (options._bellStyle.HasValue) { Options.BellStyle = options.BellStyle; } if (options._completionQueryItems.HasValue) { Options.CompletionQueryItems = options.CompletionQueryItems; } if (options.WordDelimiters != null) { Options.WordDelimiters = options.WordDelimiters; } if (options._historySearchCaseSensitive.HasValue) { Options.HistorySearchCaseSensitive = options.HistorySearchCaseSensitive; } if (options._historySaveStyle.HasValue) { Options.HistorySaveStyle = options.HistorySaveStyle; } #region vi if (options._viModeIndicator.HasValue) { Options.ViModeIndicator = options.ViModeIndicator; } #endregion if (options.HistorySavePath != null) { Options.HistorySavePath = options.HistorySavePath; if (_historyFileMutex != null) { _historyFileMutex.Dispose(); } _historyFileMutex = new Mutex(false, GetHistorySaveFileMutexName()); _historyFileLastSavedSize = 0; } if (options.ResetTokenColors) { Options.ResetColors(); } if (options._tokenKind.HasValue) { if (options._foregroundColor.HasValue) { Options.SetForegroundColor(options.TokenKind, options.ForegroundColor); } if (options._backgroundColor.HasValue) { Options.SetBackgroundColor(options.TokenKind, options.BackgroundColor); } } } private void SetKeyHandlerInternal(string[] keys, Action handler, string briefDescription, string longDescription, ScriptBlock scriptBlock) { foreach (var key in keys) { var chord = ConsoleKeyChordConverter.Convert(key); if (chord.Length == 1) { _dispatchTable[chord[0]] = MakeKeyHandler(handler, briefDescription, longDescription, scriptBlock); } else { _dispatchTable[chord[0]] = MakeKeyHandler(Chord, "ChordFirstKey"); Dictionary secondDispatchTable; if (!_chordDispatchTable.TryGetValue(chord[0], out secondDispatchTable)) { secondDispatchTable = new Dictionary(); _chordDispatchTable[chord[0]] = secondDispatchTable; } secondDispatchTable[chord[1]] = MakeKeyHandler(handler, briefDescription, longDescription, scriptBlock); } } } private void RemoveKeyHandlerInternal(string[] keys) { foreach (var key in keys) { var chord = ConsoleKeyChordConverter.Convert(key); if (chord.Length == 1) { _dispatchTable.Remove(chord[0]); } else { Dictionary secondDispatchTable; if (_chordDispatchTable.TryGetValue(chord[0], out secondDispatchTable)) { secondDispatchTable.Remove(chord[1]); if (secondDispatchTable.Count == 0) { _dispatchTable.Remove(chord[0]); } } } } } /// /// Helper function for the Set-PSReadlineOption cmdlet. /// public static void SetOptions(SetPSReadlineOption options) { _singleton.SetOptionsInternal(options); } /// /// Helper function for the Get-PSReadlineOption cmdlet. /// public static PSConsoleReadlineOptions GetOptions() { // Should we copy? It doesn't matter much, everything can be tweaked from // the cmdlet anyway. return _singleton._options; } class CustomHandlerException : Exception { internal CustomHandlerException(Exception innerException) : base("", innerException) { } } /// /// Helper function for the Set-PSReadlineKeyHandler cmdlet. /// public static void SetKeyHandler(string[] key, ScriptBlock scriptBlock, string briefDescription, string longDescription) { Action handler = (k, arg) => { try { scriptBlock.Invoke(k, arg); } catch (Exception e) { throw new CustomHandlerException(e); } }; _singleton.SetKeyHandlerInternal(key, handler, briefDescription, longDescription, scriptBlock); } /// /// Helper function for the Set-PSReadlineKeyHandler cmdlet. /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] public static void SetKeyHandler(string[] key, Action handler, string briefDescription, string longDescription) { _singleton.SetKeyHandlerInternal(key, handler, briefDescription, longDescription, null); } /// /// Helper function for the Remove-PSReadlineKeyHandler cmdlet. /// /// public static void RemoveKeyHandler(string[] key) { _singleton.RemoveKeyHandlerInternal(key); } /// /// Helper function for the Get-PSReadlineKeyHandler cmdlet. /// /// [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] public static IEnumerable GetKeyHandlers(bool includeBound = true, bool includeUnbound = false) { var boundFunctions = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var entry in _singleton._dispatchTable) { if (entry.Value.BriefDescription == "Ignore" || entry.Value.BriefDescription == "ChordFirstKey") { continue; } boundFunctions.Add(entry.Value.BriefDescription); if (includeBound) { yield return new Microsoft.PowerShell.KeyHandler { Key = entry.Key.ToGestureString(), Function = entry.Value.BriefDescription, Description = entry.Value.LongDescription, }; } } // Added to support vi command mode mappings if (_singleton._options.EditMode == EditMode.Vi) { foreach (var entry in _viCmdKeyMap) { if (entry.Value.BriefDescription == "Ignore" || entry.Value.BriefDescription == "ChordFirstKey") { continue; } boundFunctions.Add(entry.Value.BriefDescription); if (includeBound) { yield return new Microsoft.PowerShell.KeyHandler { Key = "<" + entry.Key.ToGestureString() + ">", Function = entry.Value.BriefDescription, Description = entry.Value.LongDescription, }; } } } foreach( var entry in _singleton._chordDispatchTable ) { foreach( var secondEntry in entry.Value ) { boundFunctions.Add( secondEntry.Value.BriefDescription ); if (includeBound) { yield return new Microsoft.PowerShell.KeyHandler { Key = entry.Key.ToGestureString() + "," + secondEntry.Key.ToGestureString(), Function = secondEntry.Value.BriefDescription, Description = secondEntry.Value.LongDescription, }; } } } // Added to support vi command mode chorded mappings if (_singleton._options.EditMode == EditMode.Vi) { foreach (var entry in _viCmdChordTable) { foreach (var secondEntry in entry.Value) { if (secondEntry.Value.BriefDescription == "Ignore") { continue; } boundFunctions.Add(secondEntry.Value.BriefDescription); if (includeBound) { yield return new Microsoft.PowerShell.KeyHandler { Key = "<" + entry.Key.ToGestureString() + "," + secondEntry.Key.ToGestureString() + ">", Function = secondEntry.Value.BriefDescription, Description = secondEntry.Value.LongDescription }; } } } } if (includeUnbound) { // SelfInsert isn't really unbound, but we don't want UI to show it that way boundFunctions.Add("SelfInsert"); var methods = typeof (PSConsoleReadLine).GetMethods(BindingFlags.Public | BindingFlags.Static); foreach (var method in methods) { var parameters = method.GetParameters(); if (parameters.Length != 2 || parameters[0].ParameterType != typeof (ConsoleKeyInfo?) || parameters[1].ParameterType != typeof (object)) { continue; } if (!boundFunctions.Contains(method.Name)) { yield return new Microsoft.PowerShell.KeyHandler { Key = "Unbound", Function = method.Name, Description = null, }; } } } } } }