Esempio n. 1
0
        /// <summary>
        /// Builds a sub tree under root: target framework or Dependencies node when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreeAsync(
            IProjectTree rootNode,
            TargetedDependenciesSnapshot targetedSnapshot,
            List <IDependency> dependencies,
            bool isActiveTarget,
            bool shouldCleanup)
        {
            HashSet <IProjectTree>?currentNodes = shouldCleanup
                ? new HashSet <IProjectTree>(capacity: dependencies.Count)
                : null;

            foreach (IDependency dependency in dependencies)
            {
                IProjectTree?dependencyNode      = rootNode.FindChildWithCaption(dependency.Caption);
                bool         isNewDependencyNode = dependencyNode == null;

                if (dependencyNode != null &&
                    dependency.Flags.Contains(DependencyTreeFlags.SupportsHierarchy))
                {
                    if ((dependency.Resolved && dependencyNode.Flags.Contains(DependencyTreeFlags.Unresolved)) ||
                        (!dependency.Resolved && dependencyNode.Flags.Contains(DependencyTreeFlags.Resolved)))
                    {
                        // when transition from unresolved to resolved or vise versa - remove old node
                        // and re-add new  one to allow GraphProvider to recalculate children
                        isNewDependencyNode = true;
                        rootNode            = dependencyNode.Remove();
                        dependencyNode      = null;
                    }
                }

                // NOTE this project system supports multiple implicit configuration dimensions (such as target framework)
                // which is a concept not modelled by DTE/VSLangProj. In order to produce a sensible view of the project
                // via automation, we expose only the active target framework at any given time.
                //
                // This is achieved by using IProjectItemTree for active target framework items, and IProjectTree for inactive
                // target frameworks. CPS only creates automation objects for items with "Reference" flag if they implement
                // IProjectItemTree. See SimpleItemNode.Initialize (in CPS) for details.

                dependencyNode = await CreateOrUpdateNodeAsync(
                    dependencyNode,
                    dependency,
                    targetedSnapshot,
                    isProjectItem : isActiveTarget);

                currentNodes?.Add(dependencyNode);

                IProjectTree?parent = isNewDependencyNode
                    ? rootNode.Add(dependencyNode).Parent
                    : dependencyNode.Parent;

                Assumes.NotNull(parent);

                rootNode = parent !;
            }

            return(currentNodes != null // shouldCleanup
                ? CleanupOldNodes(rootNode, currentNodes)
                : rootNode);
        }
Esempio n. 2
0
        /// <summary>
        /// Builds a sub tree under root: target framework or Dependencies node when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreeAsync(
            IProjectTree rootNode,
            ITargetedDependenciesSnapshot targetedSnapshot,
            IEnumerable <IDependency> dependencies,
            IProjectCatalogSnapshot catalogs,
            bool isActiveTarget,
            bool shouldCleanup)
        {
            var currentNodes = new List <IProjectTree>();

            foreach (IDependency dependency in dependencies)
            {
                IProjectTree dependencyNode      = rootNode.FindNodeByCaption(dependency.Caption);
                bool         isNewDependencyNode = dependencyNode == null;

                if (!isNewDependencyNode &&
                    dependency.Flags.Contains(DependencyTreeFlags.SupportsHierarchy))
                {
                    if ((dependency.Resolved && dependencyNode.Flags.Contains(DependencyTreeFlags.UnresolvedFlags)) ||
                        (!dependency.Resolved && dependencyNode.Flags.Contains(DependencyTreeFlags.ResolvedFlags)))
                    {
                        // when transition from unresolved to resolved or vise versa - remove old node
                        // and re-add new  one to allow GraphProvider to recalculate children
                        isNewDependencyNode = true;
                        rootNode            = dependencyNode.Remove();
                        dependencyNode      = null;
                    }
                }

                dependencyNode = await CreateOrUpdateNodeAsync(dependencyNode, dependency, targetedSnapshot, catalogs, isActiveTarget);

                currentNodes.Add(dependencyNode);

                if (isNewDependencyNode)
                {
                    rootNode = rootNode.Add(dependencyNode).Parent;
                }
                else
                {
                    rootNode = dependencyNode.Parent;
                }
            }

            if (shouldCleanup)
            {
                rootNode = CleanupOldNodes(rootNode, currentNodes);
            }

            return(rootNode);
        }
        /// <summary>
        /// Builds a sub tree under root: target framework or Dependencies node when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreeAsync(
            IProjectTree rootNode,
            TargetedDependenciesSnapshot targetedSnapshot,
            List <IDependency> dependencies,
            bool isActiveTarget,
            bool shouldCleanup)
        {
            HashSet <IProjectTree>?currentNodes = shouldCleanup
                ? new HashSet <IProjectTree>(capacity: dependencies.Count)
                : null;

            foreach (IDependency dependency in dependencies)
            {
                IProjectTree?dependencyNode      = rootNode.FindChildWithCaption(dependency.Caption);
                bool         isNewDependencyNode = dependencyNode == null;

                // NOTE this project system supports multiple implicit configuration dimensions (such as target framework)
                // which is a concept not modelled by DTE/VSLangProj. In order to produce a sensible view of the project
                // via automation, we expose only the active target framework at any given time.
                //
                // This is achieved by using IProjectItemTree for active target framework items, and IProjectTree for inactive
                // target frameworks. CPS only creates automation objects for items with "Reference" flag if they implement
                // IProjectItemTree. See SimpleItemNode.Initialize (in CPS) for details.

                dependencyNode = await CreateOrUpdateNodeAsync(
                    dependencyNode,
                    dependency,
                    targetedSnapshot,
                    isProjectItem : isActiveTarget);

                currentNodes?.Add(dependencyNode);

                IProjectTree?parent = isNewDependencyNode
                    ? rootNode.Add(dependencyNode).Parent
                    : dependencyNode.Parent;

                Assumes.NotNull(parent);

                rootNode = parent;
            }

            return(currentNodes != null // shouldCleanup
                ? CleanupOldNodes(rootNode, currentNodes)
                : rootNode);
        }
        /// <summary>
        /// Builds a sub tree under root: target framework or Dependencies node when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreeAsync(
            IProjectTree rootNode,
            ITargetedDependenciesSnapshot targetedSnapshot,
            List <IDependency> dependencies,
            bool isActiveTarget,
            bool shouldCleanup)
        {
            List <IProjectTree> currentNodes = shouldCleanup
                ? new List <IProjectTree>(capacity: dependencies.Count)
                : null;

            foreach (IDependency dependency in dependencies)
            {
                IProjectTree dependencyNode      = rootNode.FindChildWithCaption(dependency.Caption);
                bool         isNewDependencyNode = dependencyNode == null;

                if (!isNewDependencyNode &&
                    dependency.Flags.Contains(DependencyTreeFlags.SupportsHierarchy))
                {
                    if ((dependency.Resolved && dependencyNode.Flags.Contains(DependencyTreeFlags.UnresolvedFlags)) ||
                        (!dependency.Resolved && dependencyNode.Flags.Contains(DependencyTreeFlags.ResolvedFlags)))
                    {
                        // when transition from unresolved to resolved or vise versa - remove old node
                        // and re-add new  one to allow GraphProvider to recalculate children
                        isNewDependencyNode = true;
                        rootNode            = dependencyNode.Remove();
                        dependencyNode      = null;
                    }
                }

                dependencyNode = await CreateOrUpdateNodeAsync(dependencyNode, dependency, targetedSnapshot, isActiveTarget);

                currentNodes?.Add(dependencyNode);

                rootNode = isNewDependencyNode
                    ? rootNode.Add(dependencyNode).Parent
                    : dependencyNode.Parent;
            }

            return(shouldCleanup
                ? CleanupOldNodes(rootNode, currentNodes)
                : rootNode);
        }
Esempio n. 5
0
 /// <summary>
 /// Adds a givel list of elements to IProjectTree
 /// </summary>
 /// <param name="tree"></param>
 /// <param name="itemsToAdd"></param>
 /// <param name="itemFilter"></param>
 /// <param name="nodeCreator"></param>
 /// <returns></returns>
 public static IProjectTree AddElementsToTree(this IProjectTree tree,
                                              IEnumerable <string> itemsToAdd,
                                              Func <string, bool> itemFilter,
                                              Func <string, IProjectTree> nodeCreator)
 {
     foreach (string item in itemsToAdd)
     {
         if (itemFilter(item))
         {
             // Double check we don't already have this one -  we shouldn't
             var existingNode = tree.FindImmediateChildByPath(item);
             System.Diagnostics.Debug.Assert(existingNode == null);
             if (existingNode == null)
             {
                 tree = tree.Add(nodeCreator(item)).Parent;
             }
         }
     }
     return(tree);
 }
        /// <summary>
        /// Creates or updates a project tree for a given IProjectDependenciesSubTreeProvider
        /// </summary>
        /// <param name="dependenciesNode"></param>
        /// <param name="subTreeProvider"></param>
        /// <param name="changes"></param>
        /// <param name="catalogs">Can be null if sub tree provider does not use design time build</param>
        /// <param name="cancellationToken"></param>
        /// <returns>IProjectTree for root Dependencies node</returns>
        private IProjectTree CreateOrUpdateSubTreeProviderNode(IProjectTree dependenciesNode,
                                                               IProjectDependenciesSubTreeProvider subTreeProvider,
                                                               IDependenciesChangeDiff changes,
                                                               IProjectCatalogSnapshot catalogs,
                                                               CancellationToken cancellationToken)
        {
            Requires.NotNull(dependenciesNode, nameof(dependenciesNode));
            Requires.NotNull(subTreeProvider, nameof(subTreeProvider));
            Requires.NotNull(subTreeProvider.RootNode, nameof(subTreeProvider.RootNode));
            Requires.NotNullOrEmpty(subTreeProvider.RootNode.Caption, nameof(subTreeProvider.RootNode.Caption));
            Requires.NotNullOrEmpty(subTreeProvider.ProviderType, nameof(subTreeProvider.ProviderType));

            var projectFolder        = Path.GetDirectoryName(UnconfiguredProject.FullPath);
            var providerRootTreeNode = GetSubTreeRootNode(dependenciesNode,
                                                          subTreeProvider.RootNode.Flags);

            if (subTreeProvider.RootNode.HasChildren || subTreeProvider.ShouldBeVisibleWhenEmpty)
            {
                bool newNode = false;
                if (providerRootTreeNode == null)
                {
                    providerRootTreeNode = NewTree(
                        caption: subTreeProvider.RootNode.Caption,
                        visible: true,
                        filePath: subTreeProvider.RootNode.Id.ToString(),
                        browseObjectProperties: null,
                        flags: subTreeProvider.RootNode.Flags,
                        icon: subTreeProvider.RootNode.Icon.ToProjectSystemType(),
                        expandedIcon: subTreeProvider.RootNode.ExpandedIcon.ToProjectSystemType());

                    newNode = true;
                }

                if (changes != null)
                {
                    foreach (var removedItem in changes.RemovedNodes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(dependenciesNode);
                        }

                        var treeNode = FindProjectTreeNode(providerRootTreeNode, removedItem, projectFolder);
                        if (treeNode != null)
                        {
                            providerRootTreeNode = treeNode.Remove();
                        }
                    }

                    foreach (var updatedItem in changes.UpdatedNodes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(dependenciesNode);
                        }

                        var treeNode = FindProjectTreeNode(providerRootTreeNode, updatedItem, projectFolder);
                        if (treeNode != null)
                        {
                            var updatedNodeParentContext = GetCustomPropertyContext(treeNode.Parent);
                            var updatedValues            = new ReferencesProjectTreeCustomizablePropertyValues
                            {
                                Caption      = updatedItem.Caption,
                                Flags        = updatedItem.Flags,
                                Icon         = updatedItem.Icon.ToProjectSystemType(),
                                ExpandedIcon = updatedItem.ExpandedIcon.ToProjectSystemType()
                            };

                            ApplyProjectTreePropertiesCustomization(updatedNodeParentContext, updatedValues);

                            // update existing tree node properties
                            treeNode = treeNode.SetProperties(
                                caption: updatedItem.Caption,
                                flags: updatedItem.Flags,
                                icon: updatedItem.Icon.ToProjectSystemType(),
                                expandedIcon: updatedItem.ExpandedIcon.ToProjectSystemType());

                            providerRootTreeNode = treeNode.Parent;
                        }
                    }

                    foreach (var addedItem in changes.AddedNodes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(dependenciesNode);
                        }

                        var treeNode = FindProjectTreeNode(providerRootTreeNode, addedItem, projectFolder);
                        if (treeNode == null)
                        {
                            treeNode = CreateProjectItemTreeNode(providerRootTreeNode, addedItem, catalogs);

                            providerRootTreeNode = providerRootTreeNode.Add(treeNode).Parent;
                        }
                    }
                }

                if (newNode)
                {
                    dependenciesNode = dependenciesNode.Add(providerRootTreeNode).Parent;
                }
                else
                {
                    dependenciesNode = providerRootTreeNode.Parent;
                }
            }
            else
            {
                if (providerRootTreeNode != null)
                {
                    dependenciesNode = dependenciesNode.Remove(providerRootTreeNode);
                }
            }

            return(dependenciesNode);
        }
Esempio n. 7
0
        public async Task <IProjectTree> BuildTreeAsync(
            IProjectTree dependenciesTree,
            DependenciesSnapshot snapshot,
            CancellationToken cancellationToken = default)
        {
            // Keep a reference to the original tree to return in case we are cancelled.
            IProjectTree originalTree = dependenciesTree;

            bool hasSingleTarget = snapshot.DependenciesByTargetFramework.Count(x => !x.Key.Equals(TargetFramework.Any)) == 1;

            var currentTopLevelNodes = new HashSet <IProjectTree>();

            if (hasSingleTarget)
            {
                await BuildSingleTargetTreeAsync();
            }
            else
            {
                await BuildMultiTargetTreeAsync();
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(originalTree);
            }

            dependenciesTree = CleanupOldNodes(dependenciesTree, currentTopLevelNodes);

            ProjectImageMoniker rootIcon = _viewModelFactory.GetDependenciesRootIcon(snapshot.HasReachableVisibleUnresolvedDependency).ToProjectSystemType();

            return(dependenciesTree.SetProperties(icon: rootIcon, expandedIcon: rootIcon));

            async Task BuildSingleTargetTreeAsync()
            {
                foreach ((ITargetFramework _, TargetedDependenciesSnapshot targetedSnapshot) in snapshot.DependenciesByTargetFramework)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    dependenciesTree = await BuildSubTreesAsync(
                        rootNode : dependenciesTree,
                        snapshot.ActiveTargetFramework,
                        targetedSnapshot,
                        RememberNewNodes);
                }
            }

            async Task BuildMultiTargetTreeAsync()
            {
                foreach ((ITargetFramework targetFramework, TargetedDependenciesSnapshot targetedSnapshot) in snapshot.DependenciesByTargetFramework)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    if (targetFramework.Equals(TargetFramework.Any))
                    {
                        dependenciesTree = await BuildSubTreesAsync(
                            rootNode : dependenciesTree,
                            snapshot.ActiveTargetFramework,
                            targetedSnapshot,
                            RememberNewNodes);
                    }
                    else
                    {
                        IProjectTree?        node = dependenciesTree.FindChildWithCaption(targetFramework.FriendlyName);
                        bool                 shouldAddTargetNode = node == null;
                        IDependencyViewModel targetViewModel     = _viewModelFactory.CreateTargetViewModel(targetedSnapshot);

                        node = CreateOrUpdateNode(
                            node,
                            targetViewModel,
                            browseObjectProperties: null,
                            isProjectItem: false,
                            additionalFlags: ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp));

                        node = await BuildSubTreesAsync(
                            rootNode : node,
                            snapshot.ActiveTargetFramework,
                            targetedSnapshot,
                            CleanupOldNodes);

                        dependenciesTree = shouldAddTargetNode
                            ? dependenciesTree.Add(node).Parent !
                            : node.Parent !;

                        Assumes.NotNull(dependenciesTree);

                        currentTopLevelNodes.Add(node);
                    }
                }
            }

            IProjectTree RememberNewNodes(IProjectTree rootNode, IEnumerable <IProjectTree> currentNodes)
            {
                if (currentNodes != null)
                {
                    currentTopLevelNodes.AddRange(currentNodes);
                }

                return(rootNode);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Builds all available sub trees under root: target framework or Dependencies node
        /// when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreesAsync(
            IProjectTree rootNode,
            ITargetFramework activeTarget,
            TargetedDependenciesSnapshot targetedSnapshot,
            Func <IProjectTree, HashSet <IProjectTree>, IProjectTree> syncFunc)
        {
            var groupedByProviderType = new Dictionary <string, List <IDependency> >(StringComparers.DependencyProviderTypes);

            foreach (IDependency dependency in targetedSnapshot.TopLevelDependencies)
            {
                if (!dependency.Visible)
                {
                    // If a dependency is not visible we will still register a top-level group if it
                    // has the ShowEmptyProviderRootNode flag.
                    if (!dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode))
                    {
                        // No such flag, so skip it completely.
                        continue;
                    }
                }

                if (!groupedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies))
                {
                    dependencies = new List <IDependency>();
                    groupedByProviderType.Add(dependency.ProviderType, dependencies);
                }

                // Only add visible dependencies. See note above.
                if (dependency.Visible)
                {
                    dependencies.Add(dependency);
                }
            }

            var currentNodes = new HashSet <IProjectTree>(capacity: groupedByProviderType.Count);

            bool isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget);

            foreach ((string providerType, List <IDependency> dependencies) in groupedByProviderType)
            {
                IDependencyViewModel?subTreeViewModel = _viewModelFactory.CreateRootViewModel(
                    providerType,
                    targetedSnapshot.CheckForUnresolvedDependencies(providerType));

                if (subTreeViewModel == null)
                {
                    // In theory this should never happen, as it means we have a dependency model of a type
                    // that no provider claims. https://github.com/dotnet/project-system/issues/3653
                    continue;
                }

                IProjectTree?subTreeNode      = rootNode.FindChildWithCaption(subTreeViewModel.Caption);
                bool         isNewSubTreeNode = subTreeNode == null;

                ProjectTreeFlags excludedFlags = targetedSnapshot.TargetFramework.Equals(TargetFramework.Any)
                    ? ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp)
                    : ProjectTreeFlags.Empty;

                subTreeNode = CreateOrUpdateNode(
                    subTreeNode,
                    subTreeViewModel,
                    browseObjectProperties: null,
                    isProjectItem: false,
                    excludedFlags: excludedFlags);

                subTreeNode = await BuildSubTreeAsync(
                    subTreeNode,
                    targetedSnapshot,
                    dependencies,
                    isActiveTarget,
                    shouldCleanup : !isNewSubTreeNode);

                currentNodes.Add(subTreeNode);

                rootNode = isNewSubTreeNode
                    ? rootNode.Add(subTreeNode).Parent !
                    : subTreeNode.Parent !;

                Assumes.NotNull(rootNode);
            }

            return(syncFunc(rootNode, currentNodes));
        }
        /// <summary>
        /// Builds Dependencies tree for given dependencies snapshot
        /// </summary>
        public override async Task <IProjectTree> BuildTreeAsync(
            IProjectTree dependenciesTree,
            IDependenciesSnapshot snapshot,
            CancellationToken cancellationToken = default)
        {
            IProjectTree originalTree = dependenciesTree;

            var currentTopLevelNodes = new List <IProjectTree>();

            IProjectTree RememberNewNodes(IProjectTree rootNode, IEnumerable <IProjectTree> currentNodes)
            {
                if (currentNodes != null)
                {
                    currentTopLevelNodes.AddRange(currentNodes);
                }

                return(rootNode);
            }

            if (snapshot.Targets.Count(x => !x.Key.Equals(TargetFramework.Any)) == 1)
            {
                foreach ((ITargetFramework _, ITargetedDependenciesSnapshot targetedSnapshot) in snapshot.Targets)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(originalTree);
                    }

                    dependenciesTree = await BuildSubTreesAsync(
                        rootNode : dependenciesTree,
                        snapshot.ActiveTarget,
                        targetedSnapshot,
                        RememberNewNodes);
                }
            }
            else
            {
                foreach ((ITargetFramework targetFramework, ITargetedDependenciesSnapshot targetedSnapshot) in snapshot.Targets)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(originalTree);
                    }

                    if (targetFramework.Equals(TargetFramework.Any))
                    {
                        dependenciesTree = await BuildSubTreesAsync(
                            rootNode : dependenciesTree,
                            snapshot.ActiveTarget,
                            targetedSnapshot,
                            RememberNewNodes);
                    }
                    else
                    {
                        IProjectTree         node = dependenciesTree.FindChildWithCaption(targetFramework.FriendlyName);
                        bool                 shouldAddTargetNode = node == null;
                        IDependencyViewModel targetViewModel     = ViewModelFactory.CreateTargetViewModel(targetedSnapshot);

                        node = CreateOrUpdateNode(
                            node,
                            targetViewModel,
                            rule: null,
                            isProjectItem: false,
                            additionalFlags: ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp));

                        node = await BuildSubTreesAsync(
                            rootNode : node,
                            snapshot.ActiveTarget,
                            targetedSnapshot,
                            CleanupOldNodes);

                        dependenciesTree = shouldAddTargetNode
                            ? dependenciesTree.Add(node).Parent
                            : node.Parent;

                        currentTopLevelNodes.Add(node);
                    }
                }
            }

            dependenciesTree = CleanupOldNodes(dependenciesTree, currentTopLevelNodes);

            // now update root Dependencies node status
            ProjectImageMoniker rootIcon = ViewModelFactory.GetDependenciesRootIcon(snapshot.HasUnresolvedDependency).ToProjectSystemType();

            return(dependenciesTree.SetProperties(icon: rootIcon, expandedIcon: rootIcon));
        }
        /// <summary>
        /// Builds all available sub trees under root: target framework or Dependencies node
        /// when there is only one target.
        /// </summary>
        private async Task <IProjectTree> BuildSubTreesAsync(
            IProjectTree rootNode,
            ITargetFramework activeTarget,
            ITargetedDependenciesSnapshot targetedSnapshot,
            Func <IProjectTree, IEnumerable <IProjectTree>, IProjectTree> syncFunc)
        {
            var groupedByProviderType = new Dictionary <string, List <IDependency> >(StringComparers.DependencyProviderTypes);

            foreach (IDependency dependency in targetedSnapshot.TopLevelDependencies)
            {
                if (!dependency.Visible)
                {
                    if (dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode))
                    {
                        // if provider sends special invisible node with flag ShowEmptyProviderRootNode, we
                        // need to show provider node even if it does not have any dependencies.
                        groupedByProviderType.Add(dependency.ProviderType, new List <IDependency>());
                    }

                    continue;
                }

                if (!groupedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies))
                {
                    dependencies = new List <IDependency>();
                    groupedByProviderType.Add(dependency.ProviderType, dependencies);
                }

                dependencies.Add(dependency);
            }

            var currentNodes = new List <IProjectTree>(capacity: groupedByProviderType.Count);

            bool isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget);

            foreach ((string providerType, List <IDependency> dependencies) in groupedByProviderType)
            {
                IDependencyViewModel subTreeViewModel = ViewModelFactory.CreateRootViewModel(
                    providerType, targetedSnapshot.CheckForUnresolvedDependencies(providerType));
                IProjectTree subTreeNode      = rootNode.FindChildWithCaption(subTreeViewModel.Caption);
                bool         isNewSubTreeNode = subTreeNode == null;

                ProjectTreeFlags excludedFlags = targetedSnapshot.TargetFramework.Equals(TargetFramework.Any)
                    ? ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp)
                    : ProjectTreeFlags.Empty;

                subTreeNode = CreateOrUpdateNode(
                    subTreeNode,
                    subTreeViewModel,
                    rule: null,
                    isProjectItem: false,
                    excludedFlags: excludedFlags);

                subTreeNode = await BuildSubTreeAsync(
                    subTreeNode,
                    targetedSnapshot,
                    dependencies,
                    isActiveTarget,
                    shouldCleanup : !isNewSubTreeNode);

                currentNodes.Add(subTreeNode);

                rootNode = isNewSubTreeNode
                    ? rootNode.Add(subTreeNode).Parent
                    : subTreeNode.Parent;
            }

            return(syncFunc(rootNode, currentNodes));
        }
        /// <summary>
        /// Creates or updates a project tree for a given IProjectDependenciesSubTreeProvider
        /// </summary>
        /// <param name="dependenciesNode"></param>
        /// <param name="subTreeProvider"></param>
        /// <param name="changes"></param>
        /// <param name="catalogs">Can be null if sub tree provider does not use design time build</param>
        /// <param name="cancellationToken"></param>
        /// <returns>IProjectTree for root Dependencies node</returns>
        private IProjectTree CreateOrUpdateSubTreeProviderNode(IProjectTree dependenciesNode,
                                                               IProjectDependenciesSubTreeProvider subTreeProvider,
                                                               IDependenciesChangeDiff changes,
                                                               IProjectCatalogSnapshot catalogs,
                                                               CancellationToken cancellationToken)
        {
            Requires.NotNull(dependenciesNode, nameof(dependenciesNode));
            Requires.NotNull(subTreeProvider, nameof(subTreeProvider));
            Requires.NotNull(subTreeProvider.RootNode, nameof(subTreeProvider.RootNode));
            Requires.NotNullOrEmpty(subTreeProvider.RootNode.Caption, nameof(subTreeProvider.RootNode.Caption));
            Requires.NotNullOrEmpty(subTreeProvider.ProviderType, nameof(subTreeProvider.ProviderType));

            var providerRootTreeNode = GetSubTreeRootNode(dependenciesNode,
                                                          subTreeProvider.RootNode.Flags);

            if (subTreeProvider.RootNode.HasChildren || subTreeProvider.ShouldBeVisibleWhenEmpty)
            {
                bool newNode = false;
                if (providerRootTreeNode == null)
                {
                    providerRootTreeNode = NewTree(
                        caption: subTreeProvider.RootNode.Caption,
                        visible: true,
                        filePath: subTreeProvider.RootNode.Id.ToString(),
                        browseObjectProperties: null,
                        flags: subTreeProvider.RootNode.Flags,
                        icon: subTreeProvider.RootNode.Icon.ToProjectSystemType(),
                        expandedIcon: subTreeProvider.RootNode.ExpandedIcon.ToProjectSystemType());

                    newNode = true;
                }

                if (changes != null)
                {
                    foreach (var removedItem in changes.RemovedNodes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(dependenciesNode);
                        }

                        var treeNode = providerRootTreeNode.FindNodeByPath(removedItem.Id.ToString());
                        if (treeNode != null)
                        {
                            providerRootTreeNode = treeNode.Remove();
                        }
                    }

                    foreach (var updatedItem in changes.UpdatedNodes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(dependenciesNode);
                        }

                        var treeNode = providerRootTreeNode.FindNodeByPath(updatedItem.Id.ToString());
                        if (treeNode != null)
                        {
                            var updatedNodeParentContext = GetCustomPropertyContext(treeNode.Parent);
                            var updatedValues            = new ReferencesProjectTreeCustomizablePropertyValues
                            {
                                Caption      = updatedItem.Caption,
                                Flags        = updatedItem.Flags,
                                Icon         = updatedItem.Icon.ToProjectSystemType(),
                                ExpandedIcon = updatedItem.ExpandedIcon.ToProjectSystemType()
                            };

                            ApplyProjectTreePropertiesCustomization(updatedNodeParentContext, updatedValues);

                            // update existing tree node properties
                            treeNode = treeNode.SetProperties(
                                caption: updatedItem.Caption,
                                flags: updatedItem.Flags,
                                icon: updatedItem.Icon.ToProjectSystemType(),
                                expandedIcon: updatedItem.ExpandedIcon.ToProjectSystemType());

                            providerRootTreeNode = treeNode.Parent;
                        }
                    }

                    var configuredProjectExports = GetActiveConfiguredProjectExports(ActiveConfiguredProject);
                    foreach (var addedItem in changes.AddedNodes)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(dependenciesNode);
                        }

                        var treeNode = providerRootTreeNode.FindNodeByPath(addedItem.Id.ToString());
                        if (treeNode == null)
                        {
                            IRule rule = null;
                            if (addedItem.Properties != null)
                            {
                                // when itemSpec is not in valid absolute path format, property page does not show
                                // item name correctly.
                                var itemSpec = addedItem.Flags.Contains(DependencyNode.CustomItemSpec)
                                    ? addedItem.Caption
                                    : addedItem.Id.ItemSpec;
                                var itemContext = ProjectPropertiesContext.GetContext(UnconfiguredProject,
                                                                                      addedItem.Id.ItemType,
                                                                                      itemSpec);
                                if (addedItem.Resolved)
                                {
                                    rule = GetRuleForResolvableReference(
                                        itemContext,
                                        new KeyValuePair <string, IImmutableDictionary <string, string> >(
                                            addedItem.Id.ItemSpec, addedItem.Properties),
                                        catalogs,
                                        configuredProjectExports);
                                }
                                else
                                {
                                    rule = GetRuleForUnresolvableReference(
                                        itemContext,
                                        catalogs,
                                        configuredProjectExports);
                                }
                            }

                            // Notify about tree changes to customization context
                            var customTreePropertyContext = GetCustomPropertyContext(providerRootTreeNode);
                            var customTreePropertyValues  = new ReferencesProjectTreeCustomizablePropertyValues
                            {
                                Caption = addedItem.Caption,
                                Flags   = addedItem.Flags,
                                Icon    = addedItem.Icon.ToProjectSystemType()
                            };

                            ApplyProjectTreePropertiesCustomization(customTreePropertyContext, customTreePropertyValues);

                            treeNode = NewTree(caption: addedItem.Caption,
                                               visible: true,
                                               filePath: addedItem.Id.ToString(),
                                               browseObjectProperties: rule,
                                               flags: addedItem.Flags,
                                               icon: addedItem.Icon.ToProjectSystemType(),
                                               expandedIcon: addedItem.ExpandedIcon.ToProjectSystemType());

                            providerRootTreeNode = providerRootTreeNode.Add(treeNode).Parent;
                        }
                    }
                }

                if (newNode)
                {
                    dependenciesNode = dependenciesNode.Add(providerRootTreeNode).Parent;
                }
                else
                {
                    dependenciesNode = providerRootTreeNode.Parent;
                }
            }
            else
            {
                if (providerRootTreeNode != null)
                {
                    dependenciesNode = dependenciesNode.Remove(providerRootTreeNode);
                }
            }

            return(dependenciesNode);
        }
Esempio n. 12
0
        /// <summary>
        /// Builds Dependencies tree for given dependencies snapshot
        /// </summary>
        public override IProjectTree BuildTree(
            IProjectTree dependenciesTree,
            IDependenciesSnapshot snapshot,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            var originalTree         = dependenciesTree;
            var currentTopLevelNodes = new List <IProjectTree>();
            Func <IProjectTree, IEnumerable <IProjectTree>, IProjectTree> rememberNewNodes = (rootNode, currentNodes) =>
            {
                if (currentNodes != null)
                {
                    currentTopLevelNodes.AddRange(currentNodes);
                }

                return(rootNode);
            };

            if (snapshot.Targets.Where(x => !x.Key.Equals(TargetFramework.Any)).Count() == 1)
            {
                foreach (var target in snapshot.Targets)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(originalTree);
                    }

                    dependenciesTree = BuildSubTrees(
                        dependenciesTree,
                        snapshot.ActiveTarget,
                        target.Value,
                        target.Value.Catalogs,
                        rememberNewNodes);
                }
            }
            else
            {
                foreach (var target in snapshot.Targets)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(originalTree);
                    }

                    if (target.Key.Equals(TargetFramework.Any))
                    {
                        dependenciesTree = BuildSubTrees(dependenciesTree,
                                                         snapshot.ActiveTarget,
                                                         target.Value,
                                                         target.Value.Catalogs,
                                                         rememberNewNodes);
                    }
                    else
                    {
                        var node = dependenciesTree.FindNodeByCaption(target.Key.FriendlyName);
                        var shouldAddTargetNode = node == null;
                        var targetViewModel     = ViewModelFactory.CreateTargetViewModel(target.Value);

                        node = CreateOrUpdateNode(node,
                                                  targetViewModel,
                                                  rule: null,
                                                  isProjectItem: false,
                                                  additionalFlags: ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp));
                        node = BuildSubTrees(node, snapshot.ActiveTarget, target.Value, target.Value.Catalogs, CleanupOldNodes);

                        if (shouldAddTargetNode)
                        {
                            dependenciesTree = dependenciesTree.Add(node).Parent;
                        }
                        else
                        {
                            dependenciesTree = node.Parent;
                        }

                        currentTopLevelNodes.Add(node);
                    }
                }
            }

            dependenciesTree = CleanupOldNodes(dependenciesTree, currentTopLevelNodes);

            // now update root Dependencies node status
            var rootIcon = ViewModelFactory.GetDependenciesRootIcon(snapshot.HasUnresolvedDependency).ToProjectSystemType();

            return(dependenciesTree.SetProperties(icon: rootIcon, expandedIcon: rootIcon));
        }
Esempio n. 13
0
        /// <summary>
        /// Builds all available sub trees under root: target framework or Dependencies node
        /// when there is only one target.
        /// </summary>
        private IProjectTree BuildSubTrees(
            IProjectTree rootNode,
            ITargetFramework activeTarget,
            ITargetedDependenciesSnapshot targetedSnapshot,
            IProjectCatalogSnapshot catalogs,
            Func <IProjectTree, IEnumerable <IProjectTree>, IProjectTree> syncFunc)
        {
            var currentNodes           = new List <IProjectTree>();
            var grouppedByProviderType = new Dictionary <string, List <IDependency> >(StringComparer.OrdinalIgnoreCase);

            foreach (var dependency in targetedSnapshot.TopLevelDependencies)
            {
                if (!dependency.Visible)
                {
                    if (dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode))
                    {
                        // if provider sends special invisible node with flag ShowEmptyProviderRootNode, we
                        // need to show provider node even if it does not have any dependencies.
                        grouppedByProviderType.Add(dependency.ProviderType, new List <IDependency>());
                    }

                    continue;
                }

                if (!grouppedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies))
                {
                    dependencies = new List <IDependency>();
                    grouppedByProviderType.Add(dependency.ProviderType, dependencies);
                }

                dependencies.Add(dependency);
            }

            var isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget);

            foreach (var dependencyGroup in grouppedByProviderType)
            {
                var subTreeViewModel = ViewModelFactory.CreateRootViewModel(
                    dependencyGroup.Key, targetedSnapshot.CheckForUnresolvedDependencies(dependencyGroup.Key));
                var subTreeNode      = rootNode.FindNodeByCaption(subTreeViewModel.Caption);
                var isNewSubTreeNode = subTreeNode == null;

                var excludedFlags = ProjectTreeFlags.Empty;
                if (targetedSnapshot.TargetFramework.Equals(TargetFramework.Any))
                {
                    excludedFlags = ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp);
                }

                subTreeNode = CreateOrUpdateNode(
                    subTreeNode,
                    subTreeViewModel,
                    rule: null,
                    isProjectItem: false,
                    excludedFlags: excludedFlags);

                subTreeNode = BuildSubTree(
                    subTreeNode,
                    targetedSnapshot,
                    dependencyGroup.Value,
                    catalogs,
                    isActiveTarget,
                    shouldCleanup: !isNewSubTreeNode);

                currentNodes.Add(subTreeNode);

                if (isNewSubTreeNode)
                {
                    rootNode = rootNode.Add(subTreeNode).Parent;
                }
                else
                {
                    rootNode = subTreeNode.Parent;
                }
            }

            return(syncFunc(rootNode, currentNodes));
        }