/// <summary> /// Does actual query to Packag Index to find packages where a given type is defined. /// Note: At diagnostic level there no way to know in which document, project or workspace /// we are for a given SyntaxNode (or it's SyntaxNodeAnalysisContext) since diagnostics /// work at csc.exe level (in command line for example) and there no container objects defined /// at that time. /// Thus we will display information about all packages returned form index to suggest user where /// type can be located, however when user clicks Ctrl+. we would display only code fixes for packages /// that satisfy current project target frameworks list. /// </summary> protected override IEnumerable <string> AnalyzeNode(TIdentifierNameSyntax node) { var projectFilter = GetProjectFilter(); if (!projectFilter.IsProjectSupported(node.GetLocation().SourceTree.FilePath)) { return(null); } var typeName = node.ToString(); var packagesWithGivenType = _packageSearcher.Search(typeName); if (!packagesWithGivenType.Any()) { return(null); } var projectTargetFrameworks = _targetFrameworkProvider.GetTargetFrameworks(node.GetLocation().SourceTree.FilePath); // Note: allowHigherVersions=true here since we want to show diagnostic message for type if it exists in some package // for discoverability (tooltip) but we would not supply a code fix if package already installed in the project with // any version, user needs to upgrade on his own. // Note2: the problem here is that we don't know if type exist in older versions of the package or not and to store // all package versions in index might slow things down. If we receive feedback that we need ot be more smart here // we should consider adding all package versions to the local index. return(GetFriendlyPackagesString(TargetFrameworkHelper.GetSupportedPackages(packagesWithGivenType, projectTargetFrameworks, allowHigherVersions: true) .Take(MaxPackageSuggestions))); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; var project = document.Project; var diagnostic = diagnostics.First(); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start, findInsideTrivia: true); var ancestors = token.GetAncestors <SyntaxNode>(); if (!ancestors.Any()) { return; } var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root); if (node == null) { return; } var placeSystemNamespaceFirst = true; if (!cancellationToken.IsCancellationRequested) { if (CanAddImport(node, cancellationToken)) { var typeName = node.ToString(); var projectTargetFrameworks = _targetFrameworkProvider.GetTargetFrameworks(document.FilePath); // Note: allowHigherVersions=false here since we don't want to provide code fix that adds another // dependency for the same package but different version, user should upgrade it on his own when // see Diagnostic suggestion var packagesWithGivenType = TargetFrameworkHelper.GetSupportedPackages(_packageSearcher.Search(typeName), projectTargetFrameworks, allowHigherVersions: false) .Take(MaxPackageSuggestions); foreach (var typeInfo in packagesWithGivenType) { cancellationToken.ThrowIfCancellationRequested(); var namespaceName = typeInfo.FullName.Contains(".") ? Path.GetFileNameWithoutExtension(typeInfo.FullName) : null; if (!string.IsNullOrEmpty(namespaceName)) { var action = new AddPackageCodeAction(_packageInstaller, typeInfo, ActionTitle, (c) => AddImportAsync(node, namespaceName, document, placeSystemNamespaceFirst, cancellationToken)); context.RegisterCodeFix(action, diagnostic); } } } } }