예제 #1
0
        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));
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #11
0
 internal CommandBinding(CommandId command, KeybindingScope scope, BindingSequence sequence1, BindingSequence sequence2) : this(command, scope, sequence1)
 {
     ((List<BindingSequence>)this.Chords).Add(sequence2);
 }
예제 #12
0
        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;
        }