/// <summary>
        /// Creates the loading References folder node.
        /// </summary>
        /// <returns>a new "Dependencies" tree node.</returns>
        private IProjectTree CreateDependenciesFolder(IProjectTree oldNode)
        {
            if (oldNode == null)
            {
                var values = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = VSResources.DependenciesNodeName,
                    Icon         = ManagedImageMonikers.ReferenceGroup.ToProjectSystemType(),
                    ExpandedIcon = ManagedImageMonikers.ReferenceGroup.ToProjectSystemType(),
                    Flags        = DependencyTreeFlags.DependenciesRootNodeFlags
                };

                ApplyProjectTreePropertiesCustomization(null, values);

                // Note that all the parameters are specified so we can force this call to an
                // overload of NewTree available prior to 15.5 versions of CPS. Once a 15.5 build
                // is publicly available we can move this to an overload with default values for
                // most of the parameters, and we'll only need to pass the interesting ones.
                return(NewTree(
                           caption: values.Caption,
                           filePath: null,
                           browseObjectProperties: null,
                           icon: values.Icon,
                           expandedIcon: values.ExpandedIcon,
                           visible: true,
                           flags: values.Flags));
            }
            else
            {
                return(oldNode);
            }
        }
        /// <summary>
        /// Creates the loading References folder node.
        /// </summary>
        /// <returns>a new "Dependencies" tree node.</returns>
        private IProjectTree CreateDependenciesFolder(IProjectTree oldNode)
        {
            if (oldNode == null)
            {
                var values = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = VSResources.DependenciesNodeName,
                    Icon         = KnownMonikers.Reference.ToProjectSystemType(),
                    ExpandedIcon = KnownMonikers.Reference.ToProjectSystemType(),
                    Flags        = DependenciesRootNodeFlags
                };

                ApplyProjectTreePropertiesCustomization(null, values);

                return(NewTree(
                           values.Caption,
                           icon: values.Icon,
                           expandedIcon: values.ExpandedIcon,
                           flags: values.Flags));
            }
            else
            {
                return(oldNode);
            }
        }
 private void ApplyProjectTreePropertiesCustomization(
     IProjectTreeCustomizablePropertyContext context,
     ReferencesProjectTreeCustomizablePropertyValues values)
 {
     foreach (var provider in ProjectTreePropertiesProviders.ExtensionValues())
     {
         provider.CalculatePropertyValues(context, values);
     }
 }
Ejemplo n.º 4
0
        private IProjectTree UpdateTreeNode(
            IProjectTree node,
            IDependencyViewModel viewModel,
            IRule rule)
        {
            ProjectTreeCustomizablePropertyContext updatedNodeParentContext = GetCustomPropertyContext(node.Parent);
            var updatedValues = new ReferencesProjectTreeCustomizablePropertyValues
            {
                Caption      = viewModel.Caption,
                Flags        = viewModel.Flags,
                Icon         = viewModel.Icon.ToProjectSystemType(),
                ExpandedIcon = viewModel.ExpandedIcon.ToProjectSystemType()
            };

            ApplyProjectTreePropertiesCustomization(updatedNodeParentContext, updatedValues);

            return(node.SetProperties(
                       caption: viewModel.Caption,
                       browseObjectProperties: rule,
                       icon: viewModel.Icon.ToProjectSystemType(),
                       expandedIcon: viewModel.ExpandedIcon.ToProjectSystemType()));
        }
Ejemplo n.º 5
0
        protected override void Initialize()
        {
#pragma warning disable RS0030 // symbol LoadedProject is banned
            using (UnconfiguredProjectAsynchronousTasksService.LoadedProject())
            {
                base.Initialize();

                // this.IsApplicable may take a project lock, so we can't do it inline with this method
                // which is holding a private lock.  It turns out that doing it asynchronously isn't a problem anyway,
                // so long as we guard against races with the Dispose method.
                UnconfiguredProjectAsynchronousTasksService.LoadedProjectAsync(
                    async delegate
                {
                    await TaskScheduler.Default.SwitchTo(alwaysYield: true);
                    UnconfiguredProjectAsynchronousTasksService
                    .UnloadCancellationToken.ThrowIfCancellationRequested();

                    lock (SyncObject)
                    {
                        Verify.NotDisposed(this);

                        // Issue this token before hooking the SnapshotChanged event to prevent a race
                        // where a snapshot tree is replaced by the initial, empty tree created below.
                        // The handler will cancel this token before submitting its update.
                        CancellationToken initialTreeCancellationToken = _treeUpdateCancellationSeries.CreateNext();

                        _ = SubmitTreeUpdateAsync(
                            delegate
                        {
                            IProjectTree dependenciesNode = CreateDependenciesFolder();

                            return(Task.FromResult(new TreeUpdateResult(dependenciesNode)));
                        },
                            initialTreeCancellationToken);

                        ITargetBlock <SnapshotChangedEventArgs> actionBlock = DataflowBlockSlim.CreateActionBlock <SnapshotChangedEventArgs>(
                            e => OnDependenciesSnapshotChanged(_dependenciesSnapshotProvider, e),
                            "DependenciesProjectTreeProviderSource {1}",
                            skipIntermediateInputData: true);
                        _snapshotEventListener = _dependenciesSnapshotProvider.SnapshotChangedSource.LinkTo(actionBlock, new DataflowLinkOptions()
                        {
                            PropagateCompletion = true
                        });
                    }
                },
                    registerFaultHandler: true);
            }
#pragma warning restore RS0030 // symbol LoadedProject is banned

            IProjectTree CreateDependenciesFolder()
            {
                var values = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = Resources.DependenciesNodeName,
                    Icon         = ManagedImageMonikers.ReferenceGroup.ToProjectSystemType(),
                    ExpandedIcon = ManagedImageMonikers.ReferenceGroup.ToProjectSystemType(),
                    Flags        = DependencyTreeFlags.DependenciesRootNodeFlags
                };

                // Allow property providers to perform customization.
                // These are ordered from lowest priority to highest, allowing higher priority
                // providers to override lower priority providers.
                foreach (IProjectTreePropertiesProvider provider in _projectTreePropertiesProviders.ExtensionValues())
                {
                    provider.CalculatePropertyValues(null, values);
                }

                // Note that all the parameters are specified so we can force this call to an
                // overload of NewTree available prior to 15.5 versions of CPS. Once a 15.5 build
                // is publicly available we can move this to an overload with default values for
                // most of the parameters, and we'll only need to pass the interesting ones.
                return(NewTree(
                           caption: values.Caption,
                           filePath: null,
                           browseObjectProperties: null,
                           icon: values.Icon,
                           expandedIcon: values.ExpandedIcon,
                           visible: true,
                           flags: values.Flags));
            }
        }
        /// <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);
        }
        private IProjectItemTree CreateProjectItemTreeNode(IProjectTree providerRootTreeNode,
                                                           IDependencyNode nodeInfo,
                                                           IProjectCatalogSnapshot catalogs)
        {
            var isGenericNodeType = nodeInfo.Flags.Contains(DependencyNode.GenericDependencyFlags);
            var properties        = nodeInfo.Properties ??
                                    ImmutableDictionary <string, string> .Empty
                                    .Add(Folder.IdentityProperty, nodeInfo.Caption)
                                    .Add(Folder.FullPathProperty, string.Empty);

            // For generic node types we do set correct, known item types, however for custom nodes
            // provided by third party extensions we can not guarantee that item type will be known.
            // Thus always set predefined itemType for all custom nodes.
            // TODO: generate specific xaml rule for generic Dependency nodes
            // tracking issue: https://github.com/dotnet/roslyn-project-system/issues/1102
            var itemType = isGenericNodeType ? nodeInfo.Id.ItemType : Folder.SchemaName;

            // when itemSpec is not in valid absolute path format, property page does not show
            // item name correctly. Use real Name for the node here instead of caption, since caption
            // can have other info like version in it.
            var itemSpec = nodeInfo.Flags.Contains(DependencyNode.CustomItemSpec)
                    ? nodeInfo.Name
                    : nodeInfo.Id.ItemSpec;
            var itemContext = ProjectPropertiesContext.GetContext(UnconfiguredProject, itemType, itemSpec);
            var configuredProjectExports = GetActiveConfiguredProjectExports(ActiveConfiguredProject);

            IRule rule = null;

            if (nodeInfo.Resolved || !isGenericNodeType)
            {
                rule = GetRuleForResolvableReference(
                    itemContext,
                    new KeyValuePair <string, IImmutableDictionary <string, string> >(
                        itemSpec, properties),
                    catalogs,
                    configuredProjectExports,
                    isGenericNodeType);
            }
            else
            {
                rule = GetRuleForUnresolvableReference(
                    itemContext,
                    catalogs,
                    configuredProjectExports);
            }

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

            ApplyProjectTreePropertiesCustomization(customTreePropertyContext, customTreePropertyValues);

            var treeItemNode = NewTree(caption: nodeInfo.Caption,
                                       item: itemContext,
                                       propertySheet: null,
                                       visible: true,
                                       browseObjectProperties: rule,
                                       flags: nodeInfo.Flags,
                                       icon: nodeInfo.Icon.ToProjectSystemType(),
                                       expandedIcon: nodeInfo.ExpandedIcon.ToProjectSystemType());

            return(treeItemNode);
        }
Ejemplo n.º 8
0
        private IProjectTree CreateOrUpdateNode(
            IProjectTree?node,
            IDependencyViewModel viewModel,
            IRule?browseObjectProperties,
            bool isProjectItem,
            ProjectTreeFlags?additionalFlags = null,
            ProjectTreeFlags?excludedFlags   = null)
        {
            if (node != null)
            {
                return(UpdateTreeNode());
            }

            string?filePath = viewModel.OriginalModel != null &&
                              viewModel.OriginalModel.TopLevel &&
                              viewModel.OriginalModel.Resolved
                ? viewModel.OriginalModel.GetTopLevelId()
                : viewModel.FilePath;

            ProjectTreeFlags filteredFlags = FilterFlags(viewModel.Flags);

            return(isProjectItem
                ? CreateProjectItemTreeNode()
                : CreateProjectTreeNode());

            IProjectTree CreateProjectTreeNode()
            {
                // For IProjectTree remove ProjectTreeFlags.Common.Reference flag, otherwise CPS would fail to
                // map this node to graph node and GraphProvider would be never called.
                // Only IProjectItemTree can have this flag
                filteredFlags = filteredFlags.Except(DependencyTreeFlags.BaseReferenceFlags);

                return(_treeServices.CreateTree(
                           caption: viewModel.Caption,
                           filePath,
                           browseObjectProperties: browseObjectProperties,
                           icon: viewModel.Icon.ToProjectSystemType(),
                           expandedIcon: viewModel.ExpandedIcon.ToProjectSystemType(),
                           visible: true,
                           flags: filteredFlags));
            }

            IProjectTree CreateProjectItemTreeNode()
            {
                Assumes.NotNull(filePath);

                var itemContext = ProjectPropertiesContext.GetContext(
                    _commonServices.Project,
                    file: filePath,
                    itemType: viewModel.SchemaItemType,
                    itemName: filePath);

                return(_treeServices.CreateTree(
                           caption: viewModel.Caption,
                           itemContext: itemContext,
                           browseObjectProperties: browseObjectProperties,
                           icon: viewModel.Icon.ToProjectSystemType(),
                           expandedIcon: viewModel.ExpandedIcon.ToProjectSystemType(),
                           visible: true,
                           flags: filteredFlags));
            }

            IProjectTree UpdateTreeNode()
            {
                var updatedNodeParentContext = new ProjectTreeCustomizablePropertyContext
                {
                    ExistsOnDisk    = false,
                    ParentNodeFlags = node !.Parent?.Flags ?? default
                };

                var updatedValues = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = viewModel.Caption,
                    Flags        = viewModel.Flags,
                    Icon         = viewModel.Icon.ToProjectSystemType(),
                    ExpandedIcon = viewModel.ExpandedIcon.ToProjectSystemType()
                };

                foreach (Lazy <IProjectTreePropertiesProvider, IOrderPrecedenceMetadataView> provider in _projectTreePropertiesProviders)
                {
                    provider.Value.CalculatePropertyValues(updatedNodeParentContext, updatedValues);
                }

                return(node.SetProperties(
                           caption: updatedValues.Caption,
                           browseObjectProperties: browseObjectProperties,
                           icon: updatedValues.Icon,
                           expandedIcon: updatedValues.ExpandedIcon,
                           flags: updatedValues.Flags));
            }

            ProjectTreeFlags FilterFlags(ProjectTreeFlags flags)
            {
                if (additionalFlags.HasValue)
                {
                    flags = flags.Union(additionalFlags.Value);
                }

                if (excludedFlags.HasValue)
                {
                    flags = flags.Except(excludedFlags.Value);
                }

                return(flags);
            }
        }
        /// <summary>
        /// Generates the original references directory tree.
        /// </summary>
        protected override void Initialize()
        {
#pragma warning disable RS0030 // symbol LoadedProject is banned
            using (UnconfiguredProjectAsynchronousTasksService.LoadedProject())
#pragma warning restore RS0030
            {
#pragma warning disable RS0030 // https://github.com/dotnet/roslyn-analyzers/issues/3295
                base.Initialize();
#pragma warning restore RS0030

                // this.IsApplicable may take a project lock, so we can't do it inline with this method
                // which is holding a private lock.  It turns out that doing it asynchronously isn't a problem anyway,
                // so long as we guard against races with the Dispose method.
#pragma warning disable RS0030 // symbol LoadedProjectAsync is banned
                UnconfiguredProjectAsynchronousTasksService.LoadedProjectAsync(
#pragma warning restore RS0030
                    async delegate
                {
                    await TaskScheduler.Default.SwitchTo(alwaysYield: true);
                    UnconfiguredProjectAsynchronousTasksService
                    .UnloadCancellationToken.ThrowIfCancellationRequested();

                    lock (SyncObject)
                    {
                        Verify.NotDisposed(this);

                        // Issue this token before hooking the SnapshotChanged event to prevent a race
                        // where a snapshot tree is replaced by the initial, empty tree created below.
                        // The handler will cancel this token before submitting its update.
                        CancellationToken initialTreeCancellationToken = _treeUpdateCancellationSeries.CreateNext();

                        _ = SubmitTreeUpdateAsync(
                            delegate
                        {
                            IProjectTree dependenciesNode = CreateDependenciesNode();

                            return(Task.FromResult(new TreeUpdateResult(dependenciesNode)));
                        },
                            initialTreeCancellationToken);

                        ITargetBlock <SnapshotChangedEventArgs> actionBlock = DataflowBlockFactory.CreateActionBlock <SnapshotChangedEventArgs>(
                            OnDependenciesSnapshotChangedAsync,
                            _project,
                            nameFormat: "DependenciesProjectTreeProviderSource {1}",
                            skipIntermediateInputData: true);
                        _snapshotEventListener = _dependenciesSnapshotProvider.SnapshotChangedSource.LinkTo(actionBlock, DataflowOption.PropagateCompletion);
                    }
                },
                    registerFaultHandler: true);
            }

            IProjectTree CreateDependenciesNode()
            {
                var values = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = Resources.DependenciesNodeName,
                    Icon         = KnownMonikers.ReferenceGroup.ToProjectSystemType(),
                    ExpandedIcon = KnownMonikers.ReferenceGroup.ToProjectSystemType(),
                    Flags        = ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp)
                                   + ProjectTreeFlags.Create(ProjectTreeFlags.Common.ReferencesFolder)
                                   + ProjectTreeFlags.Create(ProjectTreeFlags.Common.VirtualFolder)
                                   + DependencyTreeFlags.DependenciesRootNode
                };

                // Allow property providers to perform customization.
                // These are ordered from lowest priority to highest, allowing higher priority
                // providers to override lower priority providers.
                foreach (IProjectTreePropertiesProvider provider in _projectTreePropertiesProviders.ExtensionValues())
                {
                    provider.CalculatePropertyValues(ProjectTreeCustomizablePropertyContext.Instance, values);
                }

                return(NewTree(
                           caption: values.Caption,
                           icon: values.Icon,
                           expandedIcon: values.ExpandedIcon,
                           flags: values.Flags));
            }
        }
        /// <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);
        }
        private IProjectTree CreateOrUpdateNode(
            IProjectTree?node,
            IDependencyViewModel viewModel,
            IRule?browseObjectProperties,
            bool isProjectItem,
            ProjectTreeFlags?additionalFlags = null,
            ProjectTreeFlags?excludedFlags   = null)
        {
            if (node != null)
            {
                return(UpdateTreeNode());
            }

            ProjectTreeFlags filteredFlags = FilterFlags(viewModel.Flags);

            return(isProjectItem
                ? CreateProjectItemTreeNode()
                : CreateProjectTreeNode());

            IProjectTree CreateProjectTreeNode()
            {
                return(_treeServices.CreateTree(
                           caption: viewModel.Caption,
                           filePath: viewModel.FilePath,
                           browseObjectProperties: browseObjectProperties,
                           icon: viewModel.Icon.ToProjectSystemType(),
                           expandedIcon: viewModel.ExpandedIcon.ToProjectSystemType(),
                           visible: true,
                           flags: filteredFlags));
            }

            IProjectTree CreateProjectItemTreeNode()
            {
                var itemContext = ProjectPropertiesContext.GetContext(
                    _commonServices.Project,
                    file: viewModel.FilePath,
                    itemType: viewModel.SchemaItemType,
                    itemName: viewModel.FilePath);

                return(_treeServices.CreateTree(
                           caption: viewModel.Caption,
                           itemContext: itemContext,
                           browseObjectProperties: browseObjectProperties,
                           icon: viewModel.Icon.ToProjectSystemType(),
                           expandedIcon: viewModel.ExpandedIcon.ToProjectSystemType(),
                           visible: true,
                           flags: filteredFlags));
            }

            IProjectTree UpdateTreeNode()
            {
                var updatedNodeParentContext = new ProjectTreeCustomizablePropertyContext
                {
                    ExistsOnDisk    = false,
                    ParentNodeFlags = node !.Parent?.Flags ?? default
                };

                var updatedValues = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = viewModel.Caption,
                    Flags        = viewModel.Flags,
                    Icon         = viewModel.Icon.ToProjectSystemType(),
                    ExpandedIcon = viewModel.ExpandedIcon.ToProjectSystemType()
                };

                foreach (Lazy <IProjectTreePropertiesProvider, IOrderPrecedenceMetadataView> provider in _projectTreePropertiesProviders)
                {
                    provider.Value.CalculatePropertyValues(updatedNodeParentContext, updatedValues);
                }

                return(node.SetProperties(
                           caption: updatedValues.Caption,
                           browseObjectProperties: browseObjectProperties,
                           icon: updatedValues.Icon,
                           expandedIcon: updatedValues.ExpandedIcon,
                           flags: updatedValues.Flags));
            }

            ProjectTreeFlags FilterFlags(ProjectTreeFlags flags)
            {
                if (additionalFlags.HasValue)
                {
                    flags = flags.Union(additionalFlags.Value);
                }

                if (excludedFlags.HasValue)
                {
                    flags = flags.Except(excludedFlags.Value);
                }

                return(flags);
            }
        }