internal CommandBinding(CommandId command, KeybindingScope scope, BindingSequence sequence) { this.Command = command; this.Scope = scope; this.Chords = new List<BindingSequence>() { sequence }; }
public CommandBinding(CommandId command, KeybindingScope scope, IEnumerable <BindingSequence> sequences) { this.Command = command; this.Scope = scope; this.Sequences = new List <BindingSequence>(sequences); }
private List <string> GetConflictListText(KeybindingScope scope, IEnumerable <BindingSequence> sequences) { // Get all conflicts for the given Scope/Shortcut (as objects) IEnumerable <BindingConflict> conflicts = GetAllConflictObjects(scope, sequences); // Add the localized text for each conflict to a list and return it. return(ConvertToLocalizedTextList(conflicts)); }
public CommandBinding(string originalDTEString, CommandId command, KeybindingScope scope, IEnumerable <BindingSequence> sequences) { this.OriginalDTEString = originalDTEString; this.Command = command; this.Scope = scope; this.Sequences = new List <BindingSequence>(sequences); }
private static string GenerateScopeText(KeybindingScope scope) { if (string.IsNullOrWhiteSpace(scope?.Name)) { return(null); } return(scope.Name); }
private IEnumerable <BindingConflict> GetAllConflictObjects(KeybindingScope scope, IEnumerable <BindingSequence> sequences) { IEnumerable <BindingConflict> conflicts = null; // Must we really assign this to run to compile? ThreadHelper.JoinableTaskFactory.Run(async() => { conflicts = await queryEngine.GetConflictsAsync(AllCommandsCache, scope, sequences); }); return(conflicts); }
private List <string> GetConflictListText(string scopeText, string shortcutText) { // Convert text to objects KeybindingScope scope = queryEngine.GetScopeByName(scopeText); IEnumerable <BindingSequence> sequences = queryEngine.GetBindingSequencesFromBindingString(shortcutText); // Get all conflicts for the given Scope/Shortcut (as objects) IEnumerable <BindingConflict> conflicts = GetAllConflictObjects(scope, sequences); // Add the localized text for each conflict to a list and return it. return(ConvertToLocalizedTextList(conflicts)); }
/// <summary> /// Parse the XML elemenent into a valid VSShortcuts object. /// /// Note: Deliberately NOT parsing the Command text. We will display the command text /// unparsed and allow it to fail at the time of import. Not all commands will always /// be available on a user's instance of VS. But it's good to show that the VSSettings /// file did include the shortcut for it. /// </summary> private VSShortcut CreateShortcutItem(XElement shortcut) { // Read values from XML definitions string operationType = shortcut.Name.LocalName; string scopeText = shortcut.Attribute("Scope").Value; string commandText = shortcut.Attribute("Command").Value; string shortcutText = shortcut.Value; // Parse the Operation type (Add or Remove shortcut) if (!ShortcutOperations.ContainsKey(operationType)) { Debug.WriteLine("Ignoring UserShortcut element: " + operationType + " with value: " + shortcut.Value); return(null); } string operationDisplayName = ShortcutOperations[operationType]; // Parse the text into known objects. Abort if text is not recognized. KeybindingScope scope = queryEngine.GetScopeByName(scopeText); if (scope == null) { Debug.WriteLine("Unable to parse scopeText: " + scopeText); return(null); } // Parse the shortcut key combinations IEnumerable <BindingSequence> sequences = queryEngine.GetBindingSequencesFromBindingString(shortcutText); if (sequences == null) { Debug.WriteLine("Unable to parse shortcutText: " + shortcutText); return(null); } // Prepare the conflict list List <string> conflictList = GetConflictListText(scope, sequences); return(new VSShortcut { Operation = operationDisplayName, Command = commandText, Scope = scopeText, Shortcut = shortcutText, Conflicts = conflictList }); }
/// <summary> /// Populates the maps that map from name -> scope info and GUID -> scope info /// </summary> private void PopulateScopeMaps() { ShellSettingsManager settingsManager = new ShellSettingsManager(this.serviceProvider); SettingsStore settingsStore = settingsManager.GetReadOnlySettingsStore(SettingsScope.Configuration); // First build map of all registered scopes if (settingsStore.CollectionExists(KeyBindingTableRegKeyName)) { int itemCount = settingsStore.GetSubCollectionCount(KeyBindingTableRegKeyName); foreach (string str in settingsStore.GetSubCollectionNames(KeyBindingTableRegKeyName)) { string collectionName = Path.Combine(KeyBindingTableRegKeyName, str); Guid scopeId; if (!Guid.TryParse(str, out scopeId)) { continue; } Guid owningPackage; uint resourceId; bool allowNavKeyBinding = false; if (scopeId == VSConstants.GUID_VSStandardCommandSet97) { owningPackage = CLSID_VsEnvironmentPackage; resourceId = ID_Intl_Base + 18; } else { if (!settingsStore.PropertyExists(collectionName, PackageRegPropertyName)) { continue; } if (!Guid.TryParse(settingsStore.GetString(collectionName, PackageRegPropertyName), out owningPackage)) { continue; } string resIdString = settingsStore.GetString(collectionName, string.Empty); if (resIdString.StartsWith("#")) { resIdString = resIdString.Substring(1); } if (!uint.TryParse(resIdString, out resourceId)) { continue; } if (settingsStore.PropertyExists(collectionName, AllowNavKeyBindingPropertyName)) { allowNavKeyBinding = settingsStore.GetUInt32(collectionName, AllowNavKeyBindingPropertyName) == 0 ? false : true; } } string scopeName; if (!ErrorHandler.Succeeded(Shell.LoadPackageString(ref owningPackage, resourceId, out scopeName))) { continue; } KeybindingScope scopeInfo = new KeybindingScope(scopeName, scopeId, allowNavKeyBinding); this.scopeGuidToScopeInfoMap[scopeId] = scopeInfo; this.scopeNameToScopeInfoMap[scopeName] = scopeInfo; } } IVsEnumGuids scopeEnum = UIShell.EnumKeyBindingScopes(); // Random GUID the shell also skips ("Source Code Text Editor" scope) Guid toSkip = new Guid("{72F42A10-B1C5-11d0-A8CD-00A0C921A4D2}"); Guid[] scopes = new Guid[1]; uint fetched = 0; while (scopeEnum.Next((uint)scopes.Length, scopes, out fetched) == VSConstants.S_OK && fetched != 0) { // We already have info for this scope if (scopeGuidToScopeInfoMap.ContainsKey(scopes[0])) { continue; } // The shell skips this as a possible binding scope if (scopes[0] == toSkip) { continue; } string path = Path.Combine("Editors", scopes[0].ToString("B")); // If it isn't a registered scope, see if it is an editor factory if (!settingsStore.CollectionExists(path)) { continue; } if (!settingsStore.PropertyExists(path, PackageRegPropertyName)) { continue; } Guid packageGuid; if (!Guid.TryParse(settingsStore.GetString(path, PackageRegPropertyName), out packageGuid)) { continue; } if (!settingsStore.PropertyExists(path, DisplayNameRegPropertyName)) { continue; } string displayNameResIdStr = settingsStore.GetString(path, DisplayNameRegPropertyName); if (displayNameResIdStr.StartsWith("#")) { displayNameResIdStr = displayNameResIdStr.Substring(1); } uint displayNameResId; if (!uint.TryParse(displayNameResIdStr, out displayNameResId)) { continue; } string displayName; if (!ErrorHandler.Succeeded(shell.LoadPackageString(ref packageGuid, displayNameResId, out displayName))) { continue; } // NOTE: Is false the right default value? KeybindingScope scopeInfo = new KeybindingScope(displayName, scopes[0], allowNavKeyBinding: false); this.scopeGuidToScopeInfoMap[scopes[0]] = scopeInfo; this.scopeNameToScopeInfoMap[displayName] = scopeInfo; } }
public async Task <IEnumerable <BindingConflict> > GetConflictsAsync(KeybindingScope scope, IEnumerable <BindingSequence> sequences) { IEnumerable <Command> commands = await GetAllCommandsAsync(); Dictionary <ConflictType, List <Tuple <CommandBinding, Command> > > conflictsMap = new Dictionary <ConflictType, List <Tuple <CommandBinding, Command> > >(); BindingSequence[] callerChord = sequences.ToArray(); bool callerIsTwoChordBinding = callerChord.Length == 2; // Populate the conflictsMap with all conflicting commands - grouped by conflict type (shadow, mask, replace) foreach (Command c in commands) { foreach (CommandBinding b in c.Bindings) { // Compare the both part of the chords - if both are 2-chord bindings if (callerIsTwoChordBinding && b.Sequences.Count == 2) { // Note: Only need to compare the second part of the chord here because the first part is always compared next. if (!SameBindingSequence(callerChord[1], b.Sequences[1])) { // This binding is a chord and the second part doesn't match the second part of the chord passed in, so ignore it continue; } } // Compare the first part of the chords if (!SameBindingSequence(callerChord[0], b.Sequences[0])) { // This binding doesn't start with the same sequence as the one give by the caller, so ignore it continue; } ConflictType conflictType; List <Tuple <CommandBinding, Command> > conflicts = null; // If the first (and possibly only) chord of the caller supplied sequence and the first (and possibly only) chord of this binding match, then there are // three possible conflicts we want to warn about (these are mututally exclusive, i.e. the given binding can only be ONE type of conflict in the list below). // // 1: If this binding is in a non-global scope, and the caller scope is global, applying the caller supplied binding will not be visible // inside this scope (ConflictType.HiddenInSomeScopes) // // 2: If this binding is in the global scope, and the caller supplied binding is NOT in the global scope, applying the caller supplied binding // will shadow it (make it inaccessible) (ConflictType.HidesGlobalBindings) // // 3: If this binding is in the same scope, applying the caller supplied binding will remove it (ConflictType.ReplacesBindings) if (!ScopeIsGlobal(b.Scope.Guid) && ScopeIsGlobal(scope.Guid)) { conflictType = ConflictType.HiddenInSomeScopes; } else if (ScopeIsGlobal(b.Scope.Guid) && !ScopeIsGlobal(scope.Guid)) { conflictType = ConflictType.HidesGlobalBindings; } else if (b.Scope.Guid == scope.Guid) { conflictType = ConflictType.ReplacesBindings; } else { // This is the case where we have a binding 'conflict' but in scopes that aren't related (or we don't know they are related) so we can ignore it continue; } // Add the conflict to the conflicts map, associated with the appropriate conflict type if (!conflictsMap.TryGetValue(conflictType, out conflicts)) { conflictsMap[conflictType] = conflicts = new List <Tuple <CommandBinding, Command> >(); } conflicts.Add(new Tuple <CommandBinding, Command>(b, c)); } } // Package the conflict data into BindingConflict objects (one for each conflict type) List <BindingConflict> result = new List <BindingConflict>(); foreach (var kvp in conflictsMap) { result.Add(new BindingConflict(kvp.Key, kvp.Value)); } return(result); }
internal CommandBinding(CommandId command, KeybindingScope scope, BindingSequence sequence1, BindingSequence sequence2) : this(command, scope, sequence1) { ((List<BindingSequence>)this.Chords).Add(sequence2); }
public async Task<IEnumerable<BindingConflict>> GetConflictsAsync(KeybindingScope scope, IEnumerable<BindingSequence> sequences) { IEnumerable<Command> commands = await GetAllCommandsAsync(); Dictionary<ConflictType, List<Tuple<CommandBinding, Command>>> conflictsMap = new Dictionary<ConflictType, List<Tuple<CommandBinding, Command>>>(); BindingSequence[] sequencesArr = sequences.ToArray(); bool isSingleChordBinding = sequencesArr.Length == 1; foreach(Command c in commands) { foreach(CommandBinding b in c.Bindings) { if(!SameBindingSequence(sequencesArr[0], b.Sequences[0])) { // This binding doesn't start with the same sequence as the one give by the caller, so ignore it continue; } ConflictType conflictType; List<Tuple<CommandBinding, Command>> conflicts = null; // If the first (and possibly only) chord of the caller supplied sequence and the first (and possibly only) chord of this binding match, then there are // three possible conflicts we want to warn about (these are mututally exclusive, i.e. the given binding can only be ONE type of conflict in the list below). // // 1: If this binding is in a non-global scope, and the caller scope is global, applying the caller supplied binding will not be visible // inside this scope (ConflictType.HiddenInSomeScopes) // // 2: If this binding is in the global scope, and the caller supplied binding is NOT in the global scope, applying the caller supplied binding // will shadow it (make it inaccessible) (ConflictType.HidesGlobalBindings) // // 3: If this binding is in the same scope, applying the caller supplied binding will remove it (ConflictType.ReplacesBindings) if(!ScopeIsGlobal(b.Scope.Guid) && ScopeIsGlobal(scope.Guid)) { conflictType = ConflictType.HiddenInSomeScopes; } else if(ScopeIsGlobal(b.Scope.Guid) && !ScopeIsGlobal(scope.Guid)) { conflictType = ConflictType.HidesGlobalBindings; } else if(b.Scope.Guid == scope.Guid) { conflictType = ConflictType.ReplacesBindings; } else { // This is the case where we have a binding 'conflict' but in scopes that aren't related (or we don't know they are related) so we can ignore it continue; } if(!conflictsMap.TryGetValue(conflictType, out conflicts)) { conflictsMap[conflictType] = conflicts = new List<Tuple<CommandBinding, Command>>(); } conflicts.Add(new Tuple<CommandBinding, Command>(b,c)); } } List<BindingConflict> result = new List<BindingConflict>(); foreach(var kvp in conflictsMap) { result.Add(new BindingConflict(kvp.Key, kvp.Value)); } return result; }