/// <summary>Search for a component in a project based on its tag name and fully qualified name.</summary>
        /// <remarks>
        /// This method makes several assumptions about the nature of components. First, it assumes that a component
        /// a given name `Name` will be located in a file `Name.razor`. Second, it assumes that the namespace the
        /// component is present in has the same name as the assembly its corresponding tag helper is loaded from.
        /// Implicitly, this method inherits any assumptions made by TrySplitNamespaceAndType.
        /// </remarks>
        /// <param name="tagHelper">A TagHelperDescriptor to find the corresponding Razor component for.</param>
        /// <returns>The corresponding DocumentSnapshot if found, null otherwise.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="tagHelper"/> is null.</exception>
        public override async Task <DocumentSnapshot?> TryLocateComponentAsync(TagHelperDescriptor tagHelper)
        {
            if (tagHelper is null)
            {
                throw new ArgumentNullException(nameof(tagHelper));
            }

            var typeName      = tagHelper.GetTypeNameIdentifier();
            var namespaceName = tagHelper.GetTypeNamespace();

            if (typeName == null || namespaceName == null)
            {
                _logger.LogWarning($"Could not split namespace and type for name {tagHelper.Name}.");
                return(null);
            }

            var lookupSymbolName = RemoveGenericContent(typeName);

            var projects = await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
                () => _projectSnapshotManager.Projects.ToArray(),
                CancellationToken.None).ConfigureAwait(false);

            foreach (var project in projects)
            {
                foreach (var path in project.DocumentFilePaths)
                {
                    // Get document and code document
                    var documentSnapshot = project.GetDocument(path);

                    // Rule out if not Razor component with correct name
                    if (!IsPathCandidateForComponent(documentSnapshot, lookupSymbolName))
                    {
                        continue;
                    }

                    var razorCodeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);

                    if (razorCodeDocument is null)
                    {
                        continue;
                    }

                    // Make sure we have the right namespace of the fully qualified name
                    if (!ComponentNamespaceMatchesFullyQualifiedName(razorCodeDocument, namespaceName))
                    {
                        continue;
                    }

                    return(documentSnapshot);
                }
            }

            return(null);
        }