public sealed override bool TryHandleRequest(IGraphContext graphContext) { if (!CanHandle(graphContext)) { return false; } bool trackChanges = false; foreach (GraphNode inputGraphNode in graphContext.InputNodes) { if (graphContext.CancelToken.IsCancellationRequested) { return trackChanges; } string? projectPath = inputGraphNode.Id.GetValue(CodeGraphNodeIdName.Assembly); if (string.IsNullOrEmpty(projectPath)) { continue; } IDependency? dependency = FindDependency(inputGraphNode, out IDependenciesSnapshot? snapshot); if (dependency == null || snapshot == null) { continue; } IDependenciesGraphViewProvider? viewProvider = FindViewProvider(dependency); if (viewProvider == null) { continue; } using var scope = new GraphTransactionScope(); ProcessInputNode(graphContext, inputGraphNode, dependency, snapshot, viewProvider, projectPath, ref trackChanges); scope.Complete(); } return trackChanges; }
/// <summary> /// ProjectContextChanged gets fired every time dependencies change for projects across solution. /// <see cref="_expandedGraphContexts"/> contains all nodes that we need to check for potential updates /// in their child dependencies. /// </summary> private void OnSnapshotChanged(object sender, SnapshotChangedEventArgs e) { DependenciesSnapshot snapshot = e.Snapshot; if (snapshot == null || e.Token.IsCancellationRequested) { return; } // _expandedGraphContexts remembers graph expanded or checked so far. // Each context represents one level in the graph, i.e. a node and its first level dependencies // Tracking changes over all expanded contexts ensures that all levels are processed // and updated when there are any changes in nodes data. lock (_lock) { foreach (IGraphContext graphContext in _expandedGraphContexts) { if (e.Token.IsCancellationRequested) { return; } bool anyChanges = false; try { if (HandleChanges(graphContext)) { anyChanges = true; } } finally { // Calling OnCompleted ensures that the changes are reflected in UI if (anyChanges) { graphContext.OnCompleted(); } } } } return; bool HandleChanges(IGraphContext graphContext) { bool anyChanges = false; foreach (GraphNode inputGraphNode in graphContext.InputNodes.ToList()) { string?existingDependencyId = inputGraphNode.GetValue <string>(DependenciesGraphSchema.DependencyIdProperty); if (string.IsNullOrEmpty(existingDependencyId)) { continue; } string?nodeProjectPath = inputGraphNode.Id.GetValue(CodeGraphNodeIdName.Assembly); if (string.IsNullOrEmpty(nodeProjectPath)) { continue; } DependenciesSnapshot?updatedSnapshot = _aggregateSnapshotProvider.GetSnapshot(nodeProjectPath !); IDependency?updatedDependency = updatedSnapshot?.FindDependency(existingDependencyId); if (updatedDependency == null) // or updatedSnapshot == null { continue; } IDependenciesGraphViewProvider?viewProvider = _viewProviders .FirstOrDefaultValue((x, d) => x.SupportsDependency(d), updatedDependency); if (viewProvider == null) { continue; } if (!viewProvider.ShouldApplyChanges(nodeProjectPath !, snapshot.ProjectPath, updatedDependency)) { continue; } using var scope = new GraphTransactionScope(); if (viewProvider.ApplyChanges( graphContext, nodeProjectPath !, updatedDependency, inputGraphNode, updatedSnapshot !.DependenciesByTargetFramework[updatedDependency.TargetFramework])) { anyChanges = true; } scope.Complete(); } return(anyChanges); } }
/// <summary> /// Generates search graph containing nodes matching search criteria in Solution Explorer /// and attaches it to correct top level node. /// </summary> private void Search(IGraphContext graphContext) { string searchParametersTypeName = typeof(ISolutionSearchParameters).GUID.ToString(); ISolutionSearchParameters searchParameters = graphContext.GetValue <ISolutionSearchParameters>(searchParametersTypeName); string?searchTerm = searchParameters?.SearchQuery.SearchString; if (searchTerm == null) { return; } var cachedDependencyToMatchingResultsMap = new Dictionary <string, HashSet <IDependency> >(StringComparer.OrdinalIgnoreCase); var searchResultsPerContext = new Dictionary <string, HashSet <IDependency> >(StringComparer.OrdinalIgnoreCase); System.Collections.Generic.IReadOnlyCollection <IDependenciesSnapshot> snapshots = AggregateSnapshotProvider.GetSnapshots(); foreach (IDependenciesSnapshot snapshot in snapshots) { searchResultsPerContext[snapshot.ProjectPath] = SearchFlat( searchTerm, snapshot); } foreach (IDependenciesSnapshot snapshot in snapshots) { IEnumerable <IDependency> allTopLevelDependencies = snapshot.GetFlatTopLevelDependencies(); HashSet <IDependency> matchedDependencies = searchResultsPerContext[snapshot.ProjectPath]; using var scope = new GraphTransactionScope(); foreach (IDependency topLevelDependency in allTopLevelDependencies) { ITargetedDependenciesSnapshot targetedSnapshot = snapshot.DependenciesByTargetFramework[topLevelDependency.TargetFramework]; if (!cachedDependencyToMatchingResultsMap .TryGetValue(topLevelDependency.Id, out HashSet <IDependency>?topLevelDependencyMatches)) { IDependenciesGraphViewProvider?viewProvider = FindViewProvider(topLevelDependency); if (viewProvider == null) { continue; } if (!viewProvider.MatchSearchResults( topLevelDependency, searchResultsPerContext, out topLevelDependencyMatches)) { if (matchedDependencies.Count == 0) { continue; } topLevelDependencyMatches = GetMatchingResultsForDependency( topLevelDependency, targetedSnapshot, matchedDependencies, cachedDependencyToMatchingResultsMap); } cachedDependencyToMatchingResultsMap[topLevelDependency.Id] = topLevelDependencyMatches; } if (topLevelDependencyMatches.Count == 0) { continue; } GraphNode topLevelNode = _builder.AddTopLevelGraphNode(graphContext, snapshot.ProjectPath, topLevelDependency.ToViewModel(targetedSnapshot)); foreach (IDependency matchedDependency in topLevelDependencyMatches) { GraphNode matchedDependencyNode = _builder.AddGraphNode(graphContext, snapshot.ProjectPath, topLevelNode, matchedDependency.ToViewModel(targetedSnapshot)); graphContext.Graph.Links.GetOrCreate(topLevelNode, matchedDependencyNode, label: null, GraphCommonSchema.Contains); } if (topLevelNode != null) { // '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 topLevelNode.AddCategory(CodeNodeCategories.ProjectItem); } } scope.Complete(); } graphContext.OnCompleted(); }