File: SolutionExplorer\DiagnosticItem\CpsUtilities.cs
Web Access
Project: ..\..\..\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation_zmmkbl53_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer
{
    internal static class CpsUtilities
    {
        /// <summary>
        /// Given the canonical name of a node representing an analyzer assembly in the
        /// CPS-based project system extracts out the full path to the assembly.
        /// </summary>
        /// <param name="projectDirectoryPath">The full path to the project directory</param>
        /// <param name="analyzerNodeCanonicalName">The canonical name of the analyzer node in the hierarchy</param>
        /// <returns>The full path to the analyzer assembly on disk, or null if <paramref name="analyzerNodeCanonicalName"/>
        /// cannot be parsed.</returns>
        /// <remarks>
        /// The canonical name takes the following form:
        /// 
        ///   [{path to project directory}\]{target framework}\analyzerdependency\{path to assembly}
        ///   
        /// e.g.:
        /// 
        ///   C:\projects\solutions\MyProj\netstandard2.0\analyzerdependency\C:\users\me\.packages\somePackage\lib\someAnalyzer.dll
        ///   
        /// This method exists solely to extract out the "path to assembly" part, i.e.
        /// "C:\users\me\.packages\somePackage\lib\someAnalyzer.dll". We don't need the
        /// other parts.
        /// 
        /// Note that the path to the project directory is optional.
        /// </remarks>
        public static string? ExtractAnalyzerFilePath(string projectDirectoryPath, string analyzerNodeCanonicalName)
        {
            // The canonical name may or may not start with the path to the project's directory.
            if (!projectDirectoryPath.EndsWith("\\"))
            {
                projectDirectoryPath += '\\';
            }
 
            if (analyzerNodeCanonicalName.StartsWith(projectDirectoryPath, StringComparison.OrdinalIgnoreCase))
            {
                // Extract the rest of the string
                analyzerNodeCanonicalName = analyzerNodeCanonicalName[projectDirectoryPath.Length..];
            }
 
            // Find the slash after the target framework
            var backslashIndex = analyzerNodeCanonicalName.IndexOf('\\');
            if (backslashIndex < 0)
            {
                return null;
            }
 
            // If the path does not contain "analyzerdependency\" immediately after the first slash, it
            // is a newer form of the analyzer tree item's file path (VS16.7) which requires no processing.
            //
            // It is theoretically possible that this incorrectly identifies an analyzer assembly
            // defined under "c:\analyzerdependency\..." as data in the old format, however this is very
            // unlikely. The side effect of such a problem is that analyzer's diagnostics would not
            // populate in the tree.
            if (analyzerNodeCanonicalName.IndexOf(@"analyzerdependency\", backslashIndex + 1, @"analyzerdependency\".Length, StringComparison.OrdinalIgnoreCase) != backslashIndex + 1)
            {
                return analyzerNodeCanonicalName;
            }
 
            // Find the slash after "analyzerdependency"
            backslashIndex = analyzerNodeCanonicalName.IndexOf('\\', backslashIndex + 1);
            if (backslashIndex < 0)
            {
                return null;
            }
 
            // The rest of the string is the path.
            return analyzerNodeCanonicalName[(backslashIndex + 1)..];
        }
    }
}