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) ? DependencyNode.GetName(nodeInfo) : 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); }
/// <summary> /// Tries to apply changes to corresponding node in given GraphContext for given changes /// </summary> private async Task TrackChangesOnGraphContextAsync(IGraphContext graphContext, ProjectContextEventArgs changes) { var updatedProjectContext = changes.Context; foreach (var inputGraphNode in graphContext.InputNodes.ToList()) { var existingNodeInfo = inputGraphNode.GetValue <IDependencyNode>( DependenciesGraphSchema.DependencyNodeProperty); if (existingNodeInfo == null) { continue; } var projectPath = GetPartialValueFromGraphNodeId(inputGraphNode.Id, CodeGraphNodeIdName.Assembly); if (string.IsNullOrEmpty(projectPath)) { continue; } if (!ShouldTrackChangesForNode(existingNodeInfo, projectPath, updatedProjectContext.ProjectFilePath)) { continue; } var subTreeProvider = await GetSubTreeProviderAsync(graphContext, inputGraphNode, projectPath, existingNodeInfo.Id).ConfigureAwait(false); if (subTreeProvider == null) { continue; } var updatedNode = subTreeProvider.GetDependencyNode(existingNodeInfo.Id); if (updatedNode == null) { continue; } var newNode = DependencyNode.Clone(existingNodeInfo); bool anyChanges = AnyChangesToTrack(newNode, updatedNode, changes.Diff, out IEnumerable <IDependencyNode> nodesToAdd, out IEnumerable <IDependencyNode> nodesToRemove); if (!anyChanges) { continue; } // register providers for new nodes outside of scope using (it can not have await there) await RegisterProviders(graphContext, projectPath, nodesToAdd).ConfigureAwait(false); using (var scope = new GraphTransactionScope()) { foreach (var nodeToRemove in nodesToRemove) { RemoveGraphNode(graphContext, projectPath, nodeToRemove, inputGraphNode); newNode.RemoveChild(nodeToRemove); } foreach (var nodeToAdd in nodesToAdd) { AddGraphNode(graphContext, projectPath, null, inputGraphNode, nodeToAdd); newNode.AddChild(nodeToAdd); } // Update the node info saved on the 'inputNode' inputGraphNode.SetValue(DependenciesGraphSchema.DependencyNodeProperty, newNode); scope.Complete(); } } }