Example #1
0
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            //Cast
            var node = (ILSpyTreeNode)selectedNodes[0];

            //Checks if the selected node is an assembly
            if (node is AssemblyTreeNode)
            {
                //Forces lazy-loading
                node.EnsureLazyChildren();

                //Checks if it's a multi-module assembly
                if (node.Children.Count > 1)
                {
                    MessageBox.Show("In a multi-module assembly select a specific module", "Select module", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                //Takes the first (and only) module
                node = (ILSpyTreeNode)node.Children[0];
            }

            //Shows the injection window
            new Injection.InjectWindow(node, 0, new InjectExistingEntry().IsVisible(selectedNodes)).ShowDialog();
        }
Example #2
0
		internal static SharpTreeNode GetNodeByVisibleIndex(SharpTreeNode root, int index)
		{
			root.GetTotalListLength(); // ensure all list lengths are calculated
			Debug.Assert(index >= 0);
			Debug.Assert(index < root.totalListLength);
			SharpTreeNode node = root;
			Debug.Assert(node != null);
			while (true) {
				if (node.left != null && index < node.left.totalListLength) {
					Debug.Assert(node.left != null);
					node = node.left;
				} else {
					if (node.left != null) {
						index -= node.left.totalListLength;
					}
					if (node.isVisible) {
						if (index == 0)
							return node;
						index--;
					}
					Debug.Assert(node.right != null);
					node = node.right;
				}
			}
		}
Example #3
0
 void ClearDescendants(SharpTreeNode node)
 {
     var index = List.IndexOf(node);
     while (index + 1 < List.Count && List[index + 1].Level > node.Level) {
         RemoveAt(index + 1);
     }
 }
Example #4
0
		public override void Delete(SharpTreeNode[] nodes)
		{
			if (MessageBox.Show("Sure?", "Delete", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
			{
				DeleteCore(nodes);
			}
		}
Example #5
0
		public override void DeleteCore(SharpTreeNode[] nodes)
		{
			foreach (var node in nodes.ToArray())
			{
				node.Parent.Children.Remove(node);
			}
		}
Example #6
0
		public override IDataObject Copy(SharpTreeNode[] nodes)
		{
			var data = new DataObject();
			var paths = SharpTreeNode.ActiveNodes.Cast<FileSystemNode>().Select(n => n.FullPath).ToArray();
			data.SetData(typeof(string[]), paths);
			return data;
		}
        protected override IMetadataTokenProvider ImportCore(MemberImportingOptions options, SharpTreeNode node)
        {
            //Checks that the task hasn't been canceled
            options.CancellationToken.ThrowIfCancellationRequested();

            //Imports and returns
            return Session.DestinationModule.Import((TypeReference)Member);
        }
		public bool IsEnabled(SharpTreeNode[] selectedNodes)
		{
			foreach (IMemberTreeNode node in selectedNodes) {
				if (!(node.Member is FieldDefinition || node.Member is MethodDefinition))
					return false;
			}
			return true;
		}
Example #9
0
 public TreeFlattener(SharpTreeNode modelRoot, bool includeRoot)
 {
     this.root = modelRoot;
     while (root.listParent != null)
         root = root.listParent;
     root.treeFlattener = this;
     this.includeRoot = includeRoot;
 }
Example #10
0
 void Insert(int index, SharpTreeNode node)
 {
     List.Insert(index, node);
     node.PropertyChanged += node_PropertyChanged;
     if (node.IsExpanded) {
         node.Children.CollectionChanged += node_ChildrenChanged;
     }
 }
		public void Execute(SharpTreeNode[] selectedNodes)
		{
			// TODO: figure out when equivalent nodes are already present
			// and focus those instead.
			foreach (IMemberTreeNode node in selectedNodes) {
				Analyze(node.Member);
			}
		}
        public SharpTreeNode GoBack(SharpTreeNode oldNode)
        {
            if (oldNode != null)
                forward.Add(oldNode);

            SharpTreeNode node = back[back.Count - 1];
            back.RemoveAt(back.Count - 1);
            return node;
        }
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            var types = selectedNodes
                .OfType<TypeTreeNode>()
                .Select(n => HAL.Converter.Type(n.TypeDefinition))
                .ToArray();

            Services.BrowseInteractions(types, true);
        }
        public SharpTreeNode GoForward(SharpTreeNode oldNode)
        {
            if (oldNode != null)
                back.Add(oldNode);

            SharpTreeNode node = forward[forward.Count - 1];
            forward.RemoveAt(forward.Count - 1);
            return node;
        }
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            var assemblyDefinitions = selectedNodes
                .OfType<AssemblyTreeNode>()
                .Select(n => n.LoadedAssembly.AssemblyDefinition)
                .ToList();

            var window = WindowManager.AssemblyBrowsers.Single();
            window.ViewModel.AddAssemblies(assemblyDefinitions);
        }
		protected override bool IsVisible(SharpTreeNode node)
		{
			return node is EventTreeNode
			       || node is FieldTreeNode
			       || node is MethodTreeNode
			       || node is PropertyTreeNode
			       || node is TypeTreeNode
				   || node is AssemblyReferenceTreeNode
			       || node is ResourceTreeNode;
		}
        private void treeView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue == true && scrollToNodeOnTreeViewVisible != null)
            {                         
                treeView.FocusNode(scrollToNodeOnTreeViewVisible);
                treeView.ScrollIntoView(scrollToNodeOnTreeViewVisible);
                scrollToNodeOnTreeViewVisible = null;
            }

        }
		public bool IsEnabled(SharpTreeNode[] selectedNodes)
		{
			foreach (IMemberTreeNode node in selectedNodes) {
				if (!(node.Member is FieldDefinition
					|| node.Member is MethodDefinition
					|| Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member)
					|| Analyzer.AnalyzedEventTreeNode.CanShow(node.Member)))
					return false;
			}
			return true;
		}
Example #19
0
		internal static bool ActivateItem(SharpTreeNode node, TypeDefinition def)
		{
			if (def != null) {
				var assemblyListNode = node.Ancestors().OfType<AssemblyListTreeNode>().FirstOrDefault();
				if (assemblyListNode != null) {
					assemblyListNode.Select(assemblyListNode.FindTypeNode(def));
					return true;
				}
			}
			return false;
		}
Example #20
0
		protected override IDataObject GetDataObject(SharpTreeNode[] nodes)
		{
			string[] data = nodes
				.OfType<XamlOutlineNode>()
				.Select(item => item.GetMarkupText())
				.ToArray();
			var dataObject = new DataObject();
			dataObject.SetData(typeof(string[]), data);
			
			return dataObject;
		}
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            var typeDefinition = selectedNodes
                .OfType<TypeTreeNode>()
                .Single().TypeDefinition;

            var window = new AncestryBrowserWindow(typeDefinition)
                         	{
                         		Owner = MainWindow.Instance
                         	};
            window.Show();
        }
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            var assemblyDefinitions = selectedNodes
                .OfType<AssemblyTreeNode>()
                .Select(n => n.LoadedAssembly.AssemblyDefinition);

            var window = new DependencyBrowserWindow(assemblyDefinitions.Select(HAL.Converter.Assembly))
            {
                Owner = Services.MainWindow
            };
            window.Show();
        }
Example #23
0
 public bool IsVisible(SharpTreeNode[] selectedNodes)
 {
     var memberNode = selectedNodes[0] as IMemberTreeNode;
     var nodeType = selectedNodes[0].GetType();
     return 
         (
             AllowedNodeTypes.Any(x => x.IsAssignableFrom(nodeType)) ||
             (memberNode != null && memberNode.Member.MetadataToken.TokenType == Mono.Cecil.TokenType.TypeDef)
         )
         &&
         !(selectedNodes[0] is ICSharpCode.ILSpy.TreeNodes.Analyzer.AnalyzerTreeNode);
 }
Example #24
0
		void UpdateDataContext(SharpTreeNode oldNode, SharpTreeNode newNode)
		{
			if (newNode != null) {
				newNode.PropertyChanged += Node_PropertyChanged;
				if (Template != null) {
					UpdateTemplate();
				}
			}
			if (oldNode != null) {
				oldNode.PropertyChanged -= Node_PropertyChanged;
			}
		}
		public void Execute(SharpTreeNode[] selectedNodes)
		{
			// TODO: figure out when equivalent nodes are already present
			// and focus those instead.
			foreach (IMemberTreeNode node in selectedNodes) {
				FieldDefinition field = node.Member as FieldDefinition;
				if (field != null)
					MainWindow.Instance.AddToAnalyzer(new AnalyzedFieldNode(field));
				MethodDefinition method = node.Member as MethodDefinition;
				if (method != null)
					MainWindow.Instance.AddToAnalyzer(new AnalyzedMethodTreeNode(method));
			}
		}
Example #26
0
		public void Execute(SharpTreeNode[] selectedNodes)
		{
			AssemblyTreeNode node = (AssemblyTreeNode)selectedNodes[0];
			AssemblyDefinition asm = node.LoadedAssembly.AssemblyDefinition as AssemblyDefinition;
			if (asm != null) {
				SaveFileDialog dlg = new SaveFileDialog();
				dlg.FileName = node.LoadedAssembly.FileName;
				dlg.Filter = "Assembly|*.dll;*.exe";
				if (dlg.ShowDialog(MainWindow.Instance) == true) {
					asm.MainModule.Write(dlg.FileName);
				}
			}
		}
Example #27
0
        protected override IMetadataTokenProvider ImportCore(MemberImportingOptions options, SharpTreeNode node)
        {
            //Checks that the task hasn't been canceled
            options.CancellationToken.ThrowIfCancellationRequested();

            //Adds the field to the destination type
            ((TypeDefinition)Destination).Fields.Add(fieldClone);
            if (_createNode)
                node.AddChildAndColorAncestors(new ILEditTreeNode(fieldClone, false));

            //Returns the new field
            return fieldClone;
        }
Example #28
0
		static DnSpyFile GetDnSpyFile(SharpTreeNode node) {
			var asmNode = node as AssemblyTreeNode;
			if (asmNode == null)
				return null;

			var module = asmNode.DnSpyFile.ModuleDef as ModuleDefMD;
			if (module == null)
				return null;
			if (!module.MetaData.PEImage.IsMemoryMappedIO)
				return null;

			return asmNode.DnSpyFile;
		}
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            var types = selectedNodes
                .OfType<TypeTreeNode>()
                .Select(n => HAL.Converter.Type(n.TypeDefinition))
                .ToArray();

            var window = new InteractionBrowserWindow(types, true)
            {
                Owner = MainWindow.Instance
            };
            window.Show();
        }
        public void Execute(SharpTreeNode[] selectedNodes)
        {
            var assemblyDefinitions = selectedNodes
                .OfType<AssemblyTreeNode>()
                .Select(n => n.LoadedAssembly.AssemblyDefinition)
                .ToList();

            var window = new AssemblyBrowserWindow(assemblyDefinitions)
                         	{
                         		Owner = MainWindow.Instance
                         	};
            window.Show();
        }
        /// <summary>
        /// Balances the subtree rooted in <paramref name="node"/> and recomputes the 'height' field.
        /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
        /// </summary>
        /// <returns>The new root node</returns>
        private static SharpTreeNode Rebalance(SharpTreeNode node)
        {
            Debug.Assert(node.left == null || Math.Abs(node.left.Balance) <= 1);
            Debug.Assert(node.right == null || Math.Abs(node.right.Balance) <= 1);

            // Keep looping until it's balanced. Not sure if this is stricly required; this is based on
            // the Rope code where node merging made this necessary.
            while (Math.Abs(node.Balance) > 1)
            {
                // AVL balancing
                // note: because we don't care about the identity of concat nodes, this works a little different than usual
                // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
                if (node.Balance > 1)
                {
                    if (node.right.Balance < 0)
                    {
                        node.right = node.right.RotateRight();
                    }

                    node = node.RotateLeft();

                    // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
                    node.left = Rebalance(node.left);
                }
                else if (node.Balance < -1)
                {
                    if (node.left.Balance > 0)
                    {
                        node.left = node.left.RotateLeft();
                    }

                    node = node.RotateRight();

                    // If 'node' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
                    node.right = Rebalance(node.right);
                }
            }

            Debug.Assert(Math.Abs(node.Balance) <= 1);
            node.height          = (byte)(1 + Math.Max(Height(node.left), Height(node.right)));
            node.totalListLength = -1; // mark for recalculation
                                       // since balancing checks the whole tree up to the root, the whole path will get marked as invalid
            return(node);
        }
        public int IndexOf(object item)
        {
            SharpTreeNode node = item as SharpTreeNode;

            if (node != null && node.IsVisible && node.GetListRoot() == root)
            {
                if (includeRoot)
                {
                    return(SharpTreeNode.GetVisibleIndexForNode(node));
                }
                else
                {
                    return(SharpTreeNode.GetVisibleIndexForNode(node) - 1);
                }
            }
            else
            {
                return(-1);
            }
        }
Example #33
0
        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "IsExpanded")
            {
                return;
            }
            SharpTreeNode node = sender as SharpTreeNode;

            if (node == null || node.Children.Count == 0)
            {
                return;
            }
            bool newValue = node.IsExpanded;
            bool oldValue = !newValue;

            RaisePropertyChangedEvent(
                ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty,
                oldValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed,
                newValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed);
        }
Example #34
0
        void UpdateAdaptor(SharpTreeNode node)
        {
            if (nodeView == null)
            {
                return;
            }

            var doAdaptor = nodeView.DataContext as SharpTreeNodeProxy;

            if (doAdaptor == null)
            {
                nodeView.DataContext = (doAdaptor = new SharpTreeNodeProxy(node));
            }
            else
            {
                doAdaptor.UpdateObject(node);
            }

            nodeView.UpdateTemplate();
        }
Example #35
0
        internal static int GetVisibleIndexForNode(SharpTreeNode node)
        {
            int index = node.left != null?node.left.GetTotalListLength() : 0;

            while (node.listParent != null)
            {
                if (node == node.listParent.right)
                {
                    if (node.listParent.left != null)
                    {
                        index += node.listParent.left.GetTotalListLength();
                    }
                    if (node.listParent.isVisible)
                    {
                        index++;
                    }
                }
                node = node.listParent;
            }
            return(index);
        }
Example #36
0
        public object this[int index] {
            get {
                if (index < 0 || index >= this.Count)
                {
                    throw new ArgumentOutOfRangeException();
                }

                SharpTreeNode node;
                if (nodeCache.TryGetValue(index, out node))
                {
                    return(node);
                }

                node             = SharpTreeNode.GetNodeByVisibleIndex(root, includeRoot ? index : index + 1);
                nodeCache[index] = node;
                return(node);
            }
            set {
                throw new NotSupportedException();
            }
        }
Example #37
0
 static void InsertNodeAfter(SharpTreeNode pos, SharpTreeNode newNode)
 {
     // newNode might be the model root of a whole subtree, so go to the list root of that subtree:
     newNode = newNode.GetListRoot();
     if (pos.right == null)
     {
         pos.right          = newNode;
         newNode.listParent = pos;
     }
     else
     {
         // insert before pos.right's leftmost:
         pos = pos.right;
         while (pos.left != null)
         {
             pos = pos.left;
         }
         Debug.Assert(pos.left == null);
         pos.left           = newNode;
         newNode.listParent = pos;
     }
     RebalanceUntilRoot(pos);
 }
Example #38
0
 SharpTreeNode Successor()
 {
     if (right != null)
     {
         SharpTreeNode node = right;
         while (node.left != null)
         {
             node = node.left;
         }
         return(node);
     }
     else
     {
         SharpTreeNode node = this;
         SharpTreeNode oldNode;
         do
         {
             oldNode = node;
             node    = node.listParent;
             // loop while we are on the way up from the right part
         } while (node != null && node.right == oldNode);
         return(node);
     }
 }
Example #39
0
        SharpTreeNode RotateRight()
        {
            /* Rotate tree to the right
             *
             *       this             left
             *       /  \             /  \
             *     left  C   ===>    A   this
             *     / \                   /  \
             *    A   B                 B    C
             */
            SharpTreeNode b      = left.right;
            SharpTreeNode newTop = left;

            if (b != null)
            {
                b.listParent = this;
            }
            this.left         = b;
            newTop.right      = this;
            newTop.listParent = this.listParent;
            this.listParent   = newTop;
            newTop.right      = Rebalance(this);
            return(newTop);
        }
        public void UpdateObject(SharpTreeNode obj)
        {
            if (Object == obj)
            {
                return;
            }

            if (Object != null)
            {
                Object.PropertyChanged -= OnPropertyChanged;
            }

            var oldNode = Object;

            Object = obj;

            if (obj == null)
            {
                IsNull = true;
            }
            else
            {
                IsNull = false;
                obj.PropertyChanged += OnPropertyChanged;

                foreach (var desc in descMap)
                {
                    desc.Value.OnValueChanged(this);
                }
            }

            if (ObjectChanged != null)
            {
                ObjectChanged(this, new ObjectChangedEventArgs(oldNode, obj));
            }
        }
Example #41
0
        /// <summary>
        /// Handles the node expanding event in the tree view.
        /// This method gets called only if the node is in the visible region (a SharpTreeNodeView exists).
        /// </summary>
        internal void HandleExpanding(SharpTreeNode node)
        {
            if (doNotScrollOnExpanding)
            {
                return;
            }

            // TODO: make sure node is materialized
            return;

            SharpTreeNode lastVisibleChild = node;

            while (true)
            {
                SharpTreeNode tmp = lastVisibleChild.Children.LastOrDefault(c => c.IsVisible);
                if (tmp != null)
                {
                    lastVisibleChild = tmp;
                }
                else
                {
                    break;
                }
            }
            if (lastVisibleChild != node)
            {
                // Make the the expanded children are visible; but don't scroll down
                // to much (keep node itself visible)
                base.ScrollIntoView(lastVisibleChild);
                // For some reason, this only works properly when delaying it...
                Dispatcher.UIThread.InvokeAsync(new Action(
                                                    delegate {
                    base.ScrollIntoView(node);
                }), DispatcherPriority.Loaded);
            }
        }
Example #42
0
 void Add(SharpTreeNode node)
 {
     Insert(List.Count, node);
 }
Example #43
0
 static void DumpTree(SharpTreeNode node)
 {
     node.GetListRoot().DumpTree();
 }
Example #44
0
 static int Height(SharpTreeNode node)
 {
     return(node != null ? node.height : 0);
 }
Example #45
0
 public TreeFlattener(SharpTreeNode root, bool includeRoot)
 {
     this.root        = root;
     this.includeRoot = includeRoot;
     List             = new ObservableCollection <SharpTreeNode>();
 }
Example #46
0
 public SharpTreeNodeProxy(SharpTreeNode obj)
 {
     UpdateObject(obj);
 }
 public ObjectChangedEventArgs(SharpTreeNode oldNode, SharpTreeNode newNode)
 {
     OldNode = oldNode;
     NewNode = newNode;
 }
Example #48
0
        private void UpdateIsVisible(bool parentIsVisible, bool updateFlattener)
        {
            bool newIsVisible = parentIsVisible && !this.isHidden;

            if (this.isVisible != newIsVisible)
            {
                this.isVisible = newIsVisible;

                // invalidate the augmented data
                SharpTreeNode node = this;
                while (node != null && node.totalListLength >= 0)
                {
                    node.totalListLength = -1;
                    node = node.listParent;
                }

                // Remember the removed nodes:
                List <SharpTreeNode> removedNodes = null;
                if (updateFlattener && !newIsVisible)
                {
                    removedNodes = this.VisibleDescendantsAndSelf().ToList();
                }

                // also update the model children:
                this.UpdateChildIsVisible(false);

                // Validate our invariants:
                if (updateFlattener)
                {
                    this.CheckRootInvariants();
                }

                // Tell the flattener about the removed nodes:
                if (removedNodes != null)
                {
                    var flattener = this.GetListRoot().treeFlattener;
                    if (flattener != null)
                    {
                        flattener.NodesRemoved(GetVisibleIndexForNode(this), removedNodes);
                        foreach (var n in removedNodes)
                        {
                            n.OnIsVisibleChanged();
                        }
                    }
                }

                // Tell the flattener about the new nodes:
                if (updateFlattener && newIsVisible)
                {
                    var flattener = this.GetListRoot().treeFlattener;
                    if (flattener != null)
                    {
                        flattener.NodesInserted(GetVisibleIndexForNode(this), this.VisibleDescendantsAndSelf());
                        foreach (var n in this.VisibleDescendantsAndSelf())
                        {
                            n.OnIsVisibleChanged();
                        }
                    }
                }
            }
        }
Example #49
0
        internal protected virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (SharpTreeNode node in e.OldItems)
                {
                    Debug.Assert(node.modelParent == this);
                    node.modelParent = null;
                    Debug.WriteLine("Removing {0} from {1}", node, this);
                    SharpTreeNode removeEnd = node;
                    while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0)
                    {
                        removeEnd = removeEnd.modelChildren.Last();
                    }

                    List <SharpTreeNode> removedNodes = null;
                    int visibleIndexOfRemoval         = 0;
                    if (node.isVisible)
                    {
                        visibleIndexOfRemoval = GetVisibleIndexForNode(node);
                        removedNodes          = node.VisibleDescendantsAndSelf().ToList();
                    }

                    RemoveNodes(node, removeEnd);

                    if (removedNodes != null)
                    {
                        var flattener = GetListRoot().treeFlattener;
                        if (flattener != null)
                        {
                            flattener.NodesRemoved(visibleIndexOfRemoval, removedNodes);
                        }
                    }
                }
            }
            if (e.NewItems != null)
            {
                SharpTreeNode insertionPos;
                if (e.NewStartingIndex == 0)
                {
                    insertionPos = null;
                }
                else
                {
                    insertionPos = modelChildren[e.NewStartingIndex - 1];
                }

                foreach (SharpTreeNode node in e.NewItems)
                {
                    Debug.Assert(node.modelParent == null);
                    node.modelParent = this;
                    node.UpdateIsVisible(isVisible && isExpanded, false);
                    //Debug.WriteLine("Inserting {0} after {1}", node, insertionPos);

                    while (insertionPos != null && insertionPos.modelChildren != null && insertionPos.modelChildren.Count > 0)
                    {
                        insertionPos = insertionPos.modelChildren.Last();
                    }
                    InsertNodeAfter(insertionPos ?? this, node);

                    insertionPos = node;
                    if (node.isVisible)
                    {
                        var flattener = GetListRoot().treeFlattener;
                        if (flattener != null)
                        {
                            flattener.NodesInserted(GetVisibleIndexForNode(node), node.VisibleDescendantsAndSelf());
                        }
                    }
                }
            }

            RaisePropertyChanged(nameof(ShowExpander));
            RaiseIsLastChangedIfNeeded(e);
        }
        private static void DeleteNode(SharpTreeNode node)
        {
            SharpTreeNode balancingNode;

            if (node.left == null)
            {
                balancingNode = node.listParent;
                node.ReplaceWith(node.right);
                node.right = null;
            }
            else if (node.right == null)
            {
                balancingNode = node.listParent;
                node.ReplaceWith(node.left);
                node.left = null;
            }
            else
            {
                SharpTreeNode tmp = node.right;
                while (tmp.left != null)
                {
                    tmp = tmp.left;
                }

                // First replace tmp with tmp.right
                balancingNode = tmp.listParent;
                tmp.ReplaceWith(tmp.right);
                tmp.right = null;
                Debug.Assert(tmp.left == null);
                Debug.Assert(tmp.listParent == null);

                // Now move node's children to tmp:
                tmp.left   = node.left;
                node.left  = null;
                tmp.right  = node.right;
                node.right = null;
                if (tmp.left != null)
                {
                    tmp.left.listParent = tmp;
                }

                if (tmp.right != null)
                {
                    tmp.right.listParent = tmp;
                }

                // Then replace node with tmp
                node.ReplaceWith(tmp);
                if (balancingNode == node)
                {
                    balancingNode = tmp;
                }
            }

            Debug.Assert(node.listParent == null);
            Debug.Assert(node.left == null);
            Debug.Assert(node.right == null);
            node.height          = 1;
            node.totalListLength = -1;
            if (balancingNode != null)
            {
                RebalanceUntilRoot(balancingNode);
            }
        }