private async Task <IProjectDependenciesSubTreeProvider> GetSubTreeProviderAsync( IGraphContext graphContext, GraphNode inputGraphNode, string projectPath, DependencyNodeId nodeId) { // Check if node has ProviderProperty set. It will be set for GraphNodes we created in // this class, but root nodes at first would have it set to null so e ould have to use // fallback File is part to get provider type for them. var subTreeProvider = inputGraphNode?.GetValue <IProjectDependenciesSubTreeProvider>( DependenciesGraphSchema.ProviderProperty); if (subTreeProvider == null) { var projectContext = ProjectContextProvider.GetProjectContext(projectPath); if (projectContext != null) { subTreeProvider = projectContext.GetProvider(nodeId.ProviderType); } } if (subTreeProvider != null) { await RegisterSubTreeProviderAsync(subTreeProvider, graphContext).ConfigureAwait(false); } return(subTreeProvider); }
/// <summary> /// Returns refreshed dependnecy node. There is a non trivial logic which is important: /// in order to find node at anypoint of time, we always fill live node instance children collection, /// which then could be found by recusrive search from RootNode. However we return always a new /// instance for requested node, to allow graph provider to compare old node instances to new ones /// when some thing changes in the dependent project and track changes. So in /// DependenciesGrpahProvider.TrackChangesOnGraphContextAsync we always remember old children /// collection for existing node, since for top level nodes (sent by CPS IProjectTree(s) we do update /// them here, which would make no difference between old and new nodes. For lower hierarchies below top /// nodes, new instances solve this problem. /// </summary> /// <param name="nodeId"></param> /// <returns></returns> public override IDependencyNode GetDependencyNode(DependencyNodeId nodeId) { if (nodeId == null) { return(null); } // normalize id to have regular paths (graph provider replaces \ with /. nodeId = nodeId.ToNormalizedId(); var node = RootNode.FindNode(nodeId, recursive: true); if (node == null) { return(null); } if (string.IsNullOrEmpty(nodeId.ContextProject)) { return(node); } var projectPath = nodeId.ContextProject; var projectContext = ProjectContextProvider.GetProjectContext(projectPath); if (projectContext == null) { return(node); } node.RemoveAllChildren(); foreach (var subTreeProvider in projectContext.GetProviders()) { if (subTreeProvider.RootNode == null || !subTreeProvider.RootNode.HasChildren) { continue; } var rootNodeChildren = subTreeProvider.RootNode.Children; foreach (var child in rootNodeChildren) { node.AddChild(DependencyNode.Clone(child)); } } // create a new instance of the node to allow graph provider to compare changes var newNode = CreateDependencyNode(node.Id, node.Priority, node.Properties, node.Resolved); newNode.AddChildren(node.Children); return(newNode); }
/// <summary> /// Generates search graph containing nodes matching search criteria in Solution Explorer /// and attaches it to correct top level node. /// </summary> private async Task SearchAsync(IGraphContext graphContext) { var searchParametersTypeName = typeof(ISolutionSearchParameters).GUID.ToString(); var searchParameters = graphContext.GetValue <ISolutionSearchParameters>(searchParametersTypeName); if (searchParameters == null) { return; } var searchTerm = searchParameters.SearchQuery.SearchString?.ToLowerInvariant(); if (searchTerm == null) { return; } var projectContexts = ProjectContextProvider.GetProjectContexts(); foreach (var projectContext in projectContexts) { var subTreeProviders = projectContext.GetProviders(); foreach (var subTreeProvider in subTreeProviders) { await RegisterSubTreeProviderAsync(subTreeProvider, graphContext).ConfigureAwait(false); foreach (var topLevelNode in subTreeProvider.RootNode.Children) { var refreshedTopLevelNode = subTreeProvider.GetDependencyNode(topLevelNode.Id); if (refreshedTopLevelNode == null) { continue; } var matchingNodes = await subTreeProvider.SearchAsync(refreshedTopLevelNode, searchTerm).ConfigureAwait(true); if (matchingNodes == null || matchingNodes.Count() <= 0) { continue; } // Note: scope should start and complete on the same thread, so we have to do it for each provider. using (var scope = new GraphTransactionScope()) { // add provider's root node to display matching nodes var topLevelGraphNode = AddTopLevelGraphNode(graphContext, projectContext.ProjectFilePath, subTreeProvider, nodeInfo: refreshedTopLevelNode); // now recursively add graph nodes for provider nodes that match search criteria under // provider's root node AddNodesToGraphRecursive(graphContext, projectContext.ProjectFilePath, subTreeProvider, topLevelGraphNode, matchingNodes); // 'node' is a GraphNode for top level dependency (which is part of solution explorer tree) // Setting ProjectItem category (and correct GraphNodeId) ensures that search graph appears // under right solution explorer hierarchy item topLevelGraphNode.AddCategory(CodeNodeCategories.ProjectItem); scope.Complete(); } } } } graphContext.OnCompleted(); }