/// <summary> /// Determine whether the element lies after the specified position. /// </summary> /// <param name="element"> /// The element. /// </param> /// <param name="position"> /// The target position. /// </param> /// <param name="xmlPositions"> /// The XML position lookup. /// </param> /// <returns> /// <c>true</c>, if the element's opening tag ("<") lies after the specified position; otherwise, <c>false</c>. /// </returns> public static bool IsAfter(this XmlElementSyntaxBase element, Position position, TextPositions xmlPositions) { if (element == null) { throw new ArgumentNullException(nameof(element)); } if (xmlPositions == null) { throw new ArgumentNullException(nameof(xmlPositions)); } Range startTokenRange; if (element is XmlEmptyElementSyntax emptyElement) { startTokenRange = emptyElement.LessThanToken.Span.ToNative(xmlPositions); } else if (element is XmlElementSyntax elementWithContent) { startTokenRange = elementWithContent.StartTag.LessThanToken.Span.ToNative(xmlPositions); } else { throw new ArgumentException($"Unexpected element kind '{element.Kind}'.", nameof(element)); } return(position <= startTokenRange); // We're still before the element when the element "at" the position is the opening tag. }
/// <summary> /// Determine whether the element lies before the specified position. /// </summary> /// <param name="element"> /// The element. /// </param> /// <param name="position"> /// The target position. /// </param> /// <param name="xmlPositions"> /// The XML position lookup. /// </param> /// <returns> /// <c>true</c>, if the element's final closing tag ("/>" or ">") lies after the specified position; otherwise, <c>false</c>. /// </returns> public static bool IsAfterPosition(this XmlElementSyntaxBase element, Position position, TextPositions xmlPositions) { if (element == null) { throw new ArgumentNullException(nameof(element)); } if (xmlPositions == null) { throw new ArgumentNullException(nameof(xmlPositions)); } Range endTokenRange; if (element is XmlEmptyElementSyntax emptyElement) { endTokenRange = emptyElement.SlashGreaterThanToken.Span.ToNative(xmlPositions); } else if (element is XmlElementSyntax elementWithContent) { endTokenRange = elementWithContent.EndTag.GreaterThanToken.Span.ToNative(xmlPositions); } else { throw new ArgumentException($"Unexpected element kind '{element.Kind}'.", nameof(element)); } return(position >= endTokenRange); }
/// <summary> /// Create a new <see cref="XSInvalidElement"/>. /// </summary> /// <param name="element"> /// The <see cref="XmlElementSyntaxBase"/> represented by the <see cref="XSInvalidElement"/>. /// </param> /// <param name="range"> /// The <see cref="Range"/>, within the source text, spanned by the element. /// </param> /// <param name="nameRange"> /// The range, within the source text, spanned by the element's name. /// </param> /// <param name="attributesRange"> /// The range, within the source text, spanned by the element's attributes. /// </param> /// <param name="parent"> /// The <see cref="XSInvalidElement"/>'s parent element (if any). /// </param> /// <param name="hasContent"> /// Does the <see cref="XSInvalidElement"/> have any content (besides attributes)? /// </param> public XSInvalidElement(XmlElementSyntaxBase element, Range range, Range nameRange, Range attributesRange, XSElement parent, bool hasContent) : base(element, range, nameRange, attributesRange, parent) { if (parent == null) { System.Diagnostics.Debugger.Break(); } HasContent = hasContent; }
/// <summary> /// Visit an <see cref="XmlDocumentSyntax"/>. /// </summary> /// <param name="document"> /// The <see cref="XmlDocumentSyntax"/>. /// </param> /// <returns> /// The <see cref="XmlDocumentSyntax"/> (unchanged). /// </returns> public override SyntaxNode VisitXmlDocument(XmlDocumentSyntax document) { XmlElementSyntaxBase root = document.Root as XmlElementSyntaxBase; if (root == null) { return(document); } if (root is XmlElementSyntax rootElement && rootElement.StartTag == null) { root = rootElement.Elements.FirstOrDefault() as XmlElementSyntaxBase; } if (root != null) { Visit(root); } return(document); }
/// <summary> /// Determine <see cref="XmlLocationFlags"/> for the current position. /// </summary> /// <returns> /// <see cref="XmlLocationFlags"/> describing the position. /// </returns> XmlLocationFlags ComputeLocationFlags(XSNode node, int absolutePosition) { if (node == null) { throw new ArgumentNullException(nameof(node)); } XmlLocationFlags flags = XmlLocationFlags.None; if (!node.IsValid) { flags |= XmlLocationFlags.Invalid; } switch (node) { case XSEmptyElement element: { flags |= XmlLocationFlags.Element | XmlLocationFlags.Empty; XmlEmptyElementSyntax syntaxNode = element.ElementNode; TextSpan nameSpan = syntaxNode.NameNode?.Span ?? new TextSpan(); if (nameSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.Name; } TextSpan attributesSpan = new TextSpan(); if (syntaxNode.SlashGreaterThanToken != null) // This is the most accurate way to measure the span of text where the element's attributes can be located. { attributesSpan = new TextSpan(start: syntaxNode.NameNode.Span.End, length: syntaxNode.SlashGreaterThanToken.Span.Start - syntaxNode.NameNode.Span.End); } else if (syntaxNode.AttributesNode != null) { attributesSpan = syntaxNode.AttributesNode.FullSpan; // We don't rely on the span of the syntaxNode.AttributesNode unless we have to, because it's often less accurate than the measurement above. } if (attributesSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.Attributes; } break; } case XSElementWithContent elementWithContent: { flags |= XmlLocationFlags.Element; XmlElementSyntax syntaxNode = elementWithContent.ElementNode; TextSpan nameSpan = syntaxNode.NameNode?.Span ?? new TextSpan(); if (nameSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.Name; } TextSpan startTagSpan = syntaxNode.StartTag?.Span ?? new TextSpan(); if (startTagSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.OpeningTag; } TextSpan attributesSpan = new TextSpan(); if (syntaxNode.StartTag?.GreaterThanToken != null) // This is the most accurate way to measure the span of text where the element's attributes can be located. { attributesSpan = new TextSpan(start: syntaxNode.NameNode.Span.End, length: syntaxNode.StartTag.GreaterThanToken.Span.Start - syntaxNode.NameNode.Span.End); } else if (syntaxNode.AttributesNode != null) { attributesSpan = syntaxNode.AttributesNode.FullSpan; // We don't rely on the span of the syntaxNode.AttributesNode unless we have to, because it's often less accurate than the measurement above. } if (attributesSpan.Contains(absolutePosition) || absolutePosition == attributesSpan.End) // In this particular case, we need an inclusive comparison. { flags |= XmlLocationFlags.Attributes; } TextSpan endTagSpan = syntaxNode.EndTag?.Span ?? new TextSpan(); if (endTagSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.ClosingTag; } if (absolutePosition >= startTagSpan.End && absolutePosition <= endTagSpan.Start) { flags |= XmlLocationFlags.Value; } break; } case XSInvalidElement invalidElement: { flags |= XmlLocationFlags.Element; XmlElementSyntaxBase syntaxNode = invalidElement.ElementNode; TextSpan nameSpan = syntaxNode.NameNode?.Span ?? new TextSpan(); if (nameSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.Name; } TextSpan attributesSpan = syntaxNode.AttributesNode?.FullSpan ?? new TextSpan(); if (attributesSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.Attributes; } break; } case XSAttribute attribute: { flags |= XmlLocationFlags.Attribute; XmlAttributeSyntax syntaxNode = attribute.AttributeNode; TextSpan nameSpan = syntaxNode.NameNode?.Span ?? new TextSpan(); if (nameSpan.Contains(absolutePosition)) { flags |= XmlLocationFlags.Name; } TextSpan valueSpan = syntaxNode.ValueNode?.Span ?? new TextSpan(); if (absolutePosition >= valueSpan.Start + 1 && absolutePosition <= valueSpan.End - 1) { flags |= XmlLocationFlags.Value; } break; } case XSElementText text: { flags |= XmlLocationFlags.Text | XmlLocationFlags.Element | XmlLocationFlags.Value; break; } case XSWhitespace whitespace: { flags |= XmlLocationFlags.Whitespace | XmlLocationFlags.Element | XmlLocationFlags.Value; break; } } return(flags); }