File: CodeModel\ExternalElements\AbstractExternalCodeElement.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.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.
 
#nullable disable
 
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Collections;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.ExternalElements;
 
public abstract class AbstractExternalCodeElement : AbstractCodeModelObject, ICodeElementContainer<AbstractExternalCodeElement>, EnvDTE.CodeElement, EnvDTE80.CodeElement2
{
    protected readonly ProjectId ProjectId;
    internal readonly SymbolKey SymbolKey;
 
    internal AbstractExternalCodeElement(CodeModelState state, ProjectId projectId, ISymbol symbol)
        : base(state)
    {
        Debug.Assert(projectId != null);
        Debug.Assert(symbol != null);
 
        this.ProjectId = projectId;
        this.SymbolKey = symbol.GetSymbolKey();
    }
 
    internal Compilation GetCompilation()
    {
        var project = this.State.Workspace.CurrentSolution.GetProject(this.ProjectId);
 
        if (project == null)
        {
            throw Exceptions.ThrowEFail();
        }
 
        return project.GetCompilationAsync(CancellationToken.None).Result;
    }
 
    internal ISymbol LookupSymbol()
    {
        var symbol = CodeModelService.ResolveSymbol(this.State.Workspace, this.ProjectId, this.SymbolKey);
 
        if (symbol == null)
        {
            throw Exceptions.ThrowEFail();
        }
 
        return symbol;
    }
 
    protected virtual EnvDTE.vsCMAccess GetAccess()
        => CodeModelService.GetAccess(LookupSymbol());
 
    private static bool TryParseDocCommentXml(string text, out XElement xml)
    {
        try
        {
            xml = XElement.Parse(text);
            return true;
        }
        catch (XmlException)
        {
            xml = null;
            return false;
        }
    }
 
    protected virtual string GetDocComment()
    {
        var symbol = LookupSymbol();
 
        if (symbol == null)
        {
            throw Exceptions.ThrowEFail();
        }
 
        var documentationCommentXml = symbol.OriginalDefinition.GetDocumentationCommentXml();
        if (string.IsNullOrWhiteSpace(documentationCommentXml))
        {
            return string.Empty;
        }
 
        if (!TryParseDocCommentXml(documentationCommentXml, out var xml))
        {
            // If we failed to parse, maybe it was because the XML fragment represents multiple elements.
            // Try surrounding with <doc></doc> and parse again.
 
            if (!TryParseDocCommentXml($"<doc>{documentationCommentXml}</doc>", out xml))
            {
                return string.Empty;
            }
        }
 
        // Surround with <doc> element. Or replace <member> element with <doc>, if it exists.
        if (xml.Name == "member")
        {
            xml.Name = "doc";
            xml.RemoveAttributes();
        }
        else if (xml.Name != "doc")
        {
            xml = new XElement("doc", xml);
        }
 
        return xml.ToString();
    }
 
    protected virtual string GetFullName()
        => CodeModelService.GetExternalSymbolFullName(LookupSymbol());
 
    protected virtual bool GetIsShared()
    {
        var symbol = LookupSymbol();
        return symbol.IsStatic;
    }
 
    protected virtual string GetName()
        => CodeModelService.GetExternalSymbolName(LookupSymbol());
 
    protected virtual object GetParent()
    {
        var symbol = LookupSymbol();
 
        if (symbol is INamespaceSymbol { IsGlobalNamespace: true })
        {
            // TODO: We should be returning the RootCodeModel object here.
            throw new NotImplementedException();
        }
 
        if (symbol.ContainingType != null)
        {
            return CodeModelService.CreateCodeType(this.State, this.ProjectId, symbol.ContainingType);
        }
        else if (symbol.ContainingNamespace != null)
        {
            return CodeModelService.CreateExternalCodeElement(this.State, this.ProjectId, symbol.ContainingNamespace);
        }
 
        throw Exceptions.ThrowEFail();
    }
 
    public EnvDTE.vsCMAccess Access
    {
        get
        {
            return GetAccess();
        }
 
        set
        {
            throw Exceptions.ThrowEFail();
        }
    }
 
    public EnvDTE.CodeElements Attributes
    {
        get { return EmptyCollection.Create(this.State, this); }
    }
 
    public virtual EnvDTE.CodeElements Children
    {
        get { throw new NotImplementedException(); }
    }
 
    EnvDTE.CodeElements ICodeElementContainer<AbstractExternalCodeElement>.GetCollection()
        => Children;
 
    protected virtual EnvDTE.CodeElements GetCollection()
        => GetCollection<AbstractExternalCodeElement>(this.Parent);
 
    public EnvDTE.CodeElements Collection
    {
        get { return GetCollection(); }
    }
 
    public string Comment
    {
        get
        {
            throw Exceptions.ThrowEFail();
        }
 
        set
        {
            throw Exceptions.ThrowEFail();
        }
    }
 
    public string DocComment
    {
        get
        {
            return GetDocComment();
        }
 
        set
        {
            throw Exceptions.ThrowEFail();
        }
    }
 
    public object Parent
    {
        get { return GetParent(); }
    }
 
    public EnvDTE.TextPoint EndPoint
    {
        get { throw Exceptions.ThrowEFail(); }
    }
 
    public string FullName
    {
        get { return GetFullName(); }
    }
 
    public bool IsShared
    {
        get
        {
            return GetIsShared();
        }
 
        set
        {
            throw Exceptions.ThrowEFail();
        }
    }
 
    public EnvDTE.TextPoint GetEndPoint(EnvDTE.vsCMPart part)
        => throw Exceptions.ThrowEFail();
 
    public EnvDTE.TextPoint GetStartPoint(EnvDTE.vsCMPart part)
        => throw Exceptions.ThrowEFail();
 
    public EnvDTE.vsCMInfoLocation InfoLocation
    {
        get { return EnvDTE.vsCMInfoLocation.vsCMInfoLocationExternal; }
    }
 
    public virtual bool IsCodeType
    {
        get { return false; }
    }
 
    public abstract EnvDTE.vsCMElement Kind { get; }
 
    public string Name
    {
        get
        {
            return GetName();
        }
 
        set
        {
            throw Exceptions.ThrowEFail();
        }
    }
 
    public EnvDTE.ProjectItem ProjectItem
    {
        get { throw Exceptions.ThrowEFail(); }
    }
 
    public EnvDTE.TextPoint StartPoint
    {
        get { throw Exceptions.ThrowEFail(); }
    }
 
    public string ExtenderCATID
    {
        get { throw new NotImplementedException(); }
    }
 
    protected virtual object GetExtenderNames()
        => throw Exceptions.ThrowENotImpl();
 
    public object ExtenderNames
    {
        get { return GetExtenderNames(); }
    }
 
    protected virtual object GetExtender(string name)
        => throw Exceptions.ThrowENotImpl();
 
    public object get_Extender(string extenderName)
        => GetExtender(extenderName);
 
    public string ElementID
    {
        get { throw new NotImplementedException(); }
    }
 
#pragma warning disable IDE0060 // Remove unused parameter - Implements interface methods for sub-types.
    public EnvDTE.CodeAttribute AddAttribute(string name, string value, object position)
        => throw Exceptions.ThrowEFail();
 
    public EnvDTE.CodeParameter AddParameter(string name, object type, object position)
        => throw Exceptions.ThrowEFail();
 
    public void RenameSymbol(string newName)
        => throw Exceptions.ThrowEFail();
 
    public void RemoveParameter(object element)
        => throw Exceptions.ThrowEFail();
 
    [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Required by interface")]
    public string get_Prototype(int flags = 0)
        => CodeModelService.GetPrototype(null, LookupSymbol(), (PrototypeFlags)flags);
#pragma warning restore IDE0060 // Remove unused parameter
}