File: CodeModel\InternalElements\AbstractKeyedCodeElement.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.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
 
/// <summary>
/// This is the base class of all code elements identified by a SyntaxNodeKey.
/// </summary>
public abstract class AbstractKeyedCodeElement : AbstractCodeElement
{
    private readonly string _name;
 
    internal AbstractKeyedCodeElement(
        CodeModelState state,
        FileCodeModel fileCodeModel,
        SyntaxNodeKey nodeKey,
        int? nodeKind)
        : base(state, fileCodeModel, nodeKind)
    {
        NodeKey = nodeKey;
        _name = null;
    }
 
    // This constructor is called for "unknown" code elements.
    internal AbstractKeyedCodeElement(
        CodeModelState state,
        FileCodeModel fileCodeModel,
        int nodeKind,
        string name)
        : base(state, fileCodeModel, nodeKind)
    {
        NodeKey = new SyntaxNodeKey(name, -1);
        _name = name;
    }
 
    internal SyntaxNodeKey NodeKey { get; private set; }
 
    internal bool IsUnknown
    {
        get { return NodeKey.Ordinal == -1; }
    }
 
    internal override SyntaxNode LookupNode()
        => CodeModelService.LookupNode(NodeKey, GetSyntaxTree());
 
    internal override bool TryLookupNode(out SyntaxNode node)
        => CodeModelService.TryLookupNode(NodeKey, GetSyntaxTree(), out node);
 
    /// <summary>
    /// This function re-acquires the key for this code element using the given syntax path.
    /// </summary>
    internal void ReacquireNodeKey(SyntaxPath syntaxPath, CancellationToken cancellationToken)
    {
        Debug.Assert(syntaxPath != null);
        if (!syntaxPath.TryResolve(GetSyntaxTree(), cancellationToken, out SyntaxNode node))
        {
            throw Exceptions.ThrowEFail();
        }
 
        var newNodeKey = CodeModelService.GetNodeKey(node);
 
        FileCodeModel.UpdateCodeElementNodeKey(this, NodeKey, newNodeKey);
 
        NodeKey = newNodeKey;
    }
 
    protected void UpdateNodeAndReacquireNodeKey<T>(Action<SyntaxNode, T> updater, T value, bool trackKinds = true)
    {
        FileCodeModel.EnsureEditor(() =>
        {
            // Sometimes, changing an element can result in needing to update its node key.
 
            var node = LookupNode();
            var nodePath = new SyntaxPath(node, trackKinds);
 
            updater(node, value);
 
            ReacquireNodeKey(nodePath, CancellationToken.None);
        });
    }
 
    protected override Document DeleteCore(Document document)
    {
        var result = base.DeleteCore(document);
 
        FileCodeModel.OnCodeElementDeleted(NodeKey);
 
        return result;
    }
 
    protected override string GetName()
    {
        if (IsUnknown)
        {
            return _name;
        }
 
        return base.GetName();
    }
 
    protected override void SetName(string value)
    {
        FileCodeModel.EnsureEditor(() =>
        {
            var nodeKeyValidation = new NodeKeyValidation();
            nodeKeyValidation.AddFileCodeModel(this.FileCodeModel);
 
            var node = LookupNode();
 
            FileCodeModel.UpdateName(node, value);
 
            nodeKeyValidation.RestoreKeys();
        });
    }
}