File: Differencing\Edit.cs
Web Access
Project: ..\..\..\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;
        private readonly EditKind _kind;
        private readonly TNode _oldNode;
        private readonly TNode _newNode;
 
        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((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 => _kind;
 
        /// <summary>
        /// Insert: 
        /// default(TNode).
        /// 
        /// Delete: 
        /// Deleted node.
        /// 
        /// Move, Update: 
        /// Node in the old tree/sequence.
        /// </summary>
        public TNode OldNode => _oldNode;
 
        /// <summary>
        /// Insert: 
        /// Inserted node.
        /// 
        /// Delete: 
        /// default(TNode)
        /// 
        /// Move, Update:
        /// Node in the new tree/sequence.
        /// </summary>
        public TNode NewNode => _newNode;
 
        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.GetSpan(node).Start;
    }
}