/// <summary> /// Dump out an attribute. /// </summary> /// <param name="attribute"> /// The target attribute. /// </param> /// <param name="depth"> /// The current element depth. /// </param> static void DumpAttribute(XAttribute attribute, int depth) { AttributeLocation location = attribute.Annotation <AttributeLocation>(); Log.Information("{Indent}@{Name}='{Value}' ({StartLine},{StartColumn}) to ({EndLine},{EndColumn})", new String(' ', depth * 4 + 4 /* Extra indent for attributes */), attribute.Name, attribute.Value, location.Start.LineNumber, location.Start.ColumnNumber, location.End.LineNumber, location.End.ColumnNumber ); }
/// <summary> /// Capture location information for the current element. /// </summary> void CaptureLocation() { Log.Verbose("[Capture{NodeType}Location] {Name} ({LineNumber},{ColumnNumber}) (IsEmpty={IsEmptyElement}, EmitEndElement={EmitEndElement}, ElementLocationStack={StackDepth})", NodeType, Name, LineNumber, LinePosition, IsEmptyElement, _emitEndElement, DumpElementStack() ); // This logic is pretty ugly, and needs serious reworking (would work better modeled as a state machine). // It's functional, there are almost certainly edge-cases it will fail (e.g. the last element). if (NodeType == XmlNodeType.Element) { ElementLocation elementLocation; if (_emitEndElement) { // Element followed by element; capture the end of the previous element. elementLocation = _elementLocationStack.Pop(); elementLocation.End = CurrentPosition.Move(columnCount: -1); Log.Verbose("[Capture{NodeType}LocationEnd] {Name} ({StartLineNumber},{StartColumnNumber}-{EndLineNumber},{EndColumnNumber})", NodeType, Name, elementLocation.Start.LineNumber, elementLocation.Start.ColumnNumber, elementLocation.End.LineNumber, elementLocation.End.ColumnNumber ); _emitEndElement = false; } Log.Verbose("[Capture{NodeType}LocationStart] {Name} ({LineNumber},{ColumnNumber})", NodeType, Name, LineNumber, LinePosition ); // Capture the start of the new element. elementLocation = new ElementLocation { Depth = Depth, Start = CurrentPosition.Move(columnCount: -1) }; _locations.Add(elementLocation); _elementLocationStack.Push(elementLocation); } else if (NodeType == XmlNodeType.EndElement || _emitEndElement) { // Element, followed by whitespace / text, followed by element. ElementLocation elementLocation = _elementLocationStack.Pop(); elementLocation.End = CurrentPosition; Log.Verbose("[Capture{NodeType}LocationEnd] {Name} ({StartLineNumber},{StartColumnNumber}-{EndLineNumber},{EndColumnNumber})", NodeType, Name, elementLocation.Start.LineNumber, elementLocation.Start.ColumnNumber, elementLocation.End.LineNumber, elementLocation.End.ColumnNumber ); _emitEndElement = false; } else if (NodeType == XmlNodeType.Attribute) { // Attribute _locations.Add( AttributeLocation.Create(CurrentPosition, Name, Value) ); } else { // We don't care, but log it anyway to help track down weird edge-case behaviour. Log.Verbose("[SkipCapture{NodeType}Location] {Name} ({LineNumber},{ColumnNumber})", NodeType, Name, LineNumber, LinePosition ); } }