File: CodeModel\InternalElements\AbstractCodeElement.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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
 
/// <summary>
/// This is the base class of all code elements.
/// </summary>
public abstract class AbstractCodeElement : AbstractCodeModelObject, ICodeElementContainer<AbstractCodeElement>, EnvDTE.CodeElement, EnvDTE80.CodeElement2
{
    private readonly ComHandle<EnvDTE.FileCodeModel, FileCodeModel> _fileCodeModel;
    private readonly int? _nodeKind;
 
    internal AbstractCodeElement(
        CodeModelState state,
        FileCodeModel fileCodeModel,
        int? nodeKind = null)
        : base(state)
    {
        Debug.Assert(fileCodeModel != null);
 
        _fileCodeModel = new ComHandle<EnvDTE.FileCodeModel, FileCodeModel>(fileCodeModel);
        _nodeKind = nodeKind;
    }
 
    internal FileCodeModel FileCodeModel
        => _fileCodeModel.Object;
 
    protected SyntaxTree GetSyntaxTree()
        => FileCodeModel.GetSyntaxTree();
 
    protected Document GetDocument()
        => FileCodeModel.GetDocument();
 
    protected SemanticModel GetSemanticModel()
        => FileCodeModel.GetSemanticModel();
 
    protected ProjectId GetProjectId()
        => FileCodeModel.GetProjectId();
 
    internal bool IsValidNode()
    {
        if (!TryLookupNode(out var node))
        {
            return false;
        }
 
        if (_nodeKind != null &&
            _nodeKind.Value != node.RawKind)
        {
            return false;
        }
 
        return true;
    }
 
    internal virtual SyntaxNode LookupNode()
    {
        if (!TryLookupNode(out var node))
        {
            throw Exceptions.ThrowEFail();
        }
 
        return node;
    }
 
    internal abstract bool TryLookupNode(out SyntaxNode node);
 
    internal virtual ISymbol LookupSymbol()
    {
        var semanticModel = GetSemanticModel();
        var node = LookupNode();
        return semanticModel.GetDeclaredSymbol(node);
    }
 
    protected void UpdateNode<T>(Action<SyntaxNode, T> updater, T value)
    {
        FileCodeModel.EnsureEditor(() =>
        {
            var node = LookupNode();
            updater(node, value);
        });
    }
 
    public abstract EnvDTE.vsCMElement Kind { get; }
 
    protected virtual string GetName()
    {
        var node = LookupNode();
        return CodeModelService.GetName(node);
    }
 
    protected virtual void SetName(string value)
        => UpdateNode(FileCodeModel.UpdateName, value);
 
    public string Name
    {
        get { return GetName(); }
        set { SetName(value); }
    }
 
    protected virtual string GetFullName()
    {
        var node = LookupNode();
        var semanticModel = GetSemanticModel();
        return CodeModelService.GetFullName(node, semanticModel);
    }
 
    public string FullName
    {
        get { return GetFullName(); }
    }
 
    public abstract object Parent { get; }
 
    public abstract EnvDTE.CodeElements Children { get; }
 
    EnvDTE.CodeElements ICodeElementContainer<AbstractCodeElement>.GetCollection()
        => Children;
 
    protected virtual EnvDTE.CodeElements GetCollection()
        => GetCollection<AbstractCodeElement>(Parent);
 
    public virtual EnvDTE.CodeElements Collection
    {
        get { return GetCollection(); }
    }
 
    private LineFormattingOptions GetLineFormattingOptions()
        => State.ThreadingContext.JoinableTaskFactory.Run(() => GetDocument().GetLineFormattingOptionsAsync(CancellationToken.None).AsTask());
 
    public EnvDTE.TextPoint StartPoint
    {
        get
        {
            var options = GetLineFormattingOptions();
            var point = CodeModelService.GetStartPoint(LookupNode(), options);
            if (point == null)
            {
                return null;
            }
 
            return FileCodeModel.TextManagerAdapter.CreateTextPoint(FileCodeModel, point.Value);
        }
    }
 
    public EnvDTE.TextPoint EndPoint
    {
        get
        {
            var options = GetLineFormattingOptions();
            var point = CodeModelService.GetEndPoint(LookupNode(), options);
            if (point == null)
            {
                return null;
            }
 
            return FileCodeModel.TextManagerAdapter.CreateTextPoint(FileCodeModel, point.Value);
        }
    }
 
    public virtual EnvDTE.TextPoint GetStartPoint(EnvDTE.vsCMPart part)
    {
        var options = GetLineFormattingOptions();
        var point = CodeModelService.GetStartPoint(LookupNode(), options, part);
        if (point == null)
        {
            return null;
        }
 
        return FileCodeModel.TextManagerAdapter.CreateTextPoint(FileCodeModel, point.Value);
    }
 
    public virtual EnvDTE.TextPoint GetEndPoint(EnvDTE.vsCMPart part)
    {
        var options = GetLineFormattingOptions();
        var point = CodeModelService.GetEndPoint(LookupNode(), options, part);
        if (point == null)
        {
            return null;
        }
 
        return FileCodeModel.TextManagerAdapter.CreateTextPoint(FileCodeModel, point.Value);
    }
 
    public virtual EnvDTE.vsCMInfoLocation InfoLocation
    {
        get
        {
            // The default implementation assumes project-located elements...
            return EnvDTE.vsCMInfoLocation.vsCMInfoLocationProject;
        }
    }
 
    public virtual bool IsCodeType
    {
        get { return false; }
    }
 
    public EnvDTE.ProjectItem ProjectItem
    {
        get { return FileCodeModel.Parent; }
    }
 
    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(); }
    }
 
    public virtual void RenameSymbol(string newName)
    {
        if (string.IsNullOrEmpty(newName))
        {
            throw new ArgumentException();
        }
 
        CodeModelService.Rename(LookupSymbol(), newName, this.Workspace, this.State.ProjectCodeModelFactory);
    }
 
    protected virtual Document DeleteCore(Document document)
    {
        var node = LookupNode();
        return CodeModelService.Delete(document, node);
    }
 
    /// <summary>
    /// Delete the element from the source file.
    /// </summary>
    internal void Delete()
    {
        FileCodeModel.PerformEdit(document =>
        {
            return DeleteCore(document);
        });
    }
 
    [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Required by interface")]
    public string get_Prototype(int flags)
        => CodeModelService.GetPrototype(LookupNode(), LookupSymbol(), (PrototypeFlags)flags);
}