// Finds a node/edge pair matching a given symbol offset in the tree. // If the pair matches a character within a text node, the text node is split. internal void GetNodeAndEdgeAtOffset(int offset, bool splitNode, out SplayTreeNode node, out ElementEdge edge) { int nodeOffset; int siblingTreeOffset; bool checkZeroWidthNode; // Offset zero/SymbolCount-1 are before/after the root node, which // is an illegal position -- you can't add or remove content there // and it's never exposed publicly. Invariant.Assert(offset >= 1 && offset <= this.InternalSymbolCount - 1, "Bogus symbol offset!"); // If this flag is set true on exit, we need to consider the case // where we've found a "zero-width" (SymbolCount == 0) text node. // Zero width nodes needs special handling, since they are logically // part of a following or preceding node. checkZeroWidthNode = false; // Find the node. node = _rootNode; nodeOffset = 0; // Each iteration walks through one tree. while (true) { // While we're at it, fix up the node's SymbolOffsetCache, // since we're doing the work already. Invariant.Assert(node.Generation != _rootNode.Generation || node.SymbolOffsetCache == -1 || node.SymbolOffsetCache == nodeOffset, "Bad node offset cache!"); node.Generation = _rootNode.Generation; node.SymbolOffsetCache = nodeOffset; if (offset == nodeOffset) { edge = ElementEdge.BeforeStart; checkZeroWidthNode = true; break; } if (node is TextTreeRootNode || node is TextTreeTextElementNode) { if (offset == nodeOffset + 1) { edge = ElementEdge.AfterStart; break; } if (offset == nodeOffset + node.SymbolCount - 1) { edge = ElementEdge.BeforeEnd; break; } } if (offset == nodeOffset + node.SymbolCount) { edge = ElementEdge.AfterEnd; checkZeroWidthNode = true; break; } // No child node? That means we're inside a TextTreeTextNode. if (node.ContainedNode == null) { Invariant.Assert(node is TextTreeTextNode); // Need to split the TextTreeTextNode. // Here we want a character buried inside a single node, split // the node open.... if (splitNode) { node = ((TextTreeTextNode)node).Split(offset - nodeOffset, ElementEdge.AfterEnd); } edge = ElementEdge.BeforeStart; break; } // Need to look into one of the child nodes. node = node.ContainedNode; nodeOffset += 1; // Skip over the parent element start edge. // Walk down the sibling tree. node = node.GetSiblingAtOffset(offset - nodeOffset, out siblingTreeOffset); nodeOffset += siblingTreeOffset; } // If we're on a zero-width TextTreeTextNode we need some special handling. if (checkZeroWidthNode) { node = AdjustForZeroWidthNode(node, edge); } }