File: Differencing\Edit.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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 Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Differencing;
 
/// <summary>
/// Represents an edit operation on a tree or a sequence of nodes.
/// </summary>
/// <typeparam name="TNode">Tree node.</typeparam>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
public readonly struct Edit<TNode> : IEquatable<Edit<TNode>>
{
    private readonly TreeComparer<TNode> _comparer;
 
    internal Edit(EditKind kind, TreeComparer<TNode> comparer, TNode oldNode, TNode newNode)
    {
        Debug.Assert((oldNode == null || oldNode.Equals(null)) == (kind == EditKind.Insert));
        Debug.Assert((newNode == null || newNode.Equals(null)) == (kind == EditKind.Delete));
 
        Debug.Assert(comparer == null ||
                     oldNode == null || oldNode.Equals(null) ||
                     newNode == null || newNode.Equals(null) ||
                     !comparer.TreesEqual(oldNode, newNode));
 
        _comparer = comparer;
        Kind = kind;
        OldNode = oldNode;
        NewNode = newNode;
    }
 
    public EditKind Kind { get; }
 
    /// <summary>
    /// Insert: 
    /// default(TNode).
    /// 
    /// Delete: 
    /// Deleted node.
    /// 
    /// Move, Update: 
    /// Node in the old tree/sequence.
    /// </summary>
    public TNode OldNode { get; }
 
    /// <summary>
    /// Insert: 
    /// Inserted node.
    /// 
    /// Delete: 
    /// default(TNode)
    /// 
    /// Move, Update:
    /// Node in the new tree/sequence.
    /// </summary>
    public TNode NewNode { get; }
 
    public override bool Equals(object obj)
        => obj is Edit<TNode> && Equals((Edit<TNode>)obj);
 
    public bool Equals(Edit<TNode> other)
    {
        return Kind == other.Kind
            && (OldNode == null) ? other.OldNode == null : OldNode.Equals(other.OldNode)
            && (NewNode == null) ? other.NewNode == null : NewNode.Equals(other.NewNode);
    }
 
    public override int GetHashCode()
    {
        var hash = (int)Kind;
        if (OldNode != null)
        {
            hash = Hash.Combine(OldNode.GetHashCode(), hash);
        }
 
        if (NewNode != null)
        {
            hash = Hash.Combine(NewNode.GetHashCode(), hash);
        }
 
        return hash;
    }
 
    // Has to be 'internal' for now as it's used by EnC test tool
    internal string GetDebuggerDisplay()
    {
        var result = Kind.ToString();
        switch (Kind)
        {
            case EditKind.Delete:
                return result + " [" + OldNode.ToString() + "]" + DisplayPosition(OldNode);
 
            case EditKind.Insert:
                return result + " [" + NewNode.ToString() + "]" + DisplayPosition(NewNode);
 
            case EditKind.Update:
                return result + " [" + OldNode.ToString() + "]" + DisplayPosition(OldNode) + " -> [" + NewNode.ToString() + "]" + DisplayPosition(NewNode);
 
            case EditKind.Move:
            case EditKind.Reorder:
                return result + " [" + OldNode.ToString() + "]" + DisplayPosition(OldNode) + " -> " + DisplayPosition(NewNode);
        }
 
        return result;
    }
 
    private string DisplayPosition(TNode node)
        => (_comparer != null) ? "@" + _comparer.GetSpan(node).Start : "";
}