Example #1
0
        /// <summary>
        ///     Perform post-parse processing on the node to ensure that <see cref="ExpressionNode.Range"/>s are populated and children are connected via the usual relationships (<see cref="ExpressionNode.Parent"/>, <see cref="ExpressionNode.PreviousSibling"/>, and <see cref="ExpressionNode.NextSibling"/>).
        /// </summary>
        /// <typeparam name="TNode">
        ///     The root node type.
        /// </typeparam>
        /// <param name="root">
        ///     The root node.
        /// </param>
        /// <param name="textPositions">
        ///     A <see cref="TextPositions"/> used to map absolute node positions to line / column.
        /// </param>
        /// <returns>
        ///     The root node (enables inline use).
        /// </returns>
        public static TNode PostParse <TNode>(this TNode root, TextPositions textPositions)
            where TNode : ExpressionNode
        {
            if (root == null)
            {
                throw new System.ArgumentNullException(nameof(root));
            }

            if (textPositions == null)
            {
                throw new System.ArgumentNullException(nameof(textPositions));
            }

            Dictionary <int, Position> positionCache = new Dictionary <int, Position>();

            void SetRange(ExpressionNode node)
            {
                Position start;

                if (!positionCache.TryGetValue(node.AbsoluteStart, out start))
                {
                    start = textPositions.GetPosition(node.AbsoluteStart);
                    positionCache.Add(node.AbsoluteStart, start);
                }

                Position end;

                if (!positionCache.TryGetValue(node.AbsoluteEnd, out end))
                {
                    end = textPositions.GetPosition(node.AbsoluteEnd);
                    positionCache.Add(node.AbsoluteEnd, end);
                }

                node.Range = new Range(start, end);
            }

            SetRange(root);

            foreach (ExpressionContainerNode parent in root.DescendantNodes().OfType <ExpressionContainerNode>())
            {
                SetRange(root);

                ExpressionNode previousSibling = null;
                foreach (ExpressionNode nextSibling in parent.Children)
                {
                    SetRange(nextSibling);

                    nextSibling.Parent          = parent;
                    nextSibling.PreviousSibling = previousSibling;
                    if (previousSibling != null)
                    {
                        previousSibling.NextSibling = nextSibling;
                    }

                    previousSibling = nextSibling;
                }
            }

            return(root);
        }
        /// <summary>
        ///     Convert the <see cref="TextSpan"/> to its native equivalent.
        /// </summary>
        /// <param name="span">
        ///     The <see cref="TextSpan"/> to convert.
        /// </param>
        /// <param name="textPositions">
        ///     The textual position lookup used to map absolute positions to lines and columns.
        /// </param>
        /// <returns>
        ///     The equivalent <see cref="Range"/>.
        /// </returns>
        public static Range ToNative(this TextSpan span, TextPositions textPositions)
        {
            if (textPositions == null)
            {
                throw new ArgumentNullException(nameof(textPositions));
            }

            Position startPosition = textPositions.GetPosition(span.Start);
            Position endPosition   = textPositions.GetPosition(span.End);

            if (endPosition.ColumnNumber == 0)
            {
                throw new InvalidOperationException("Should not happen anymore");
            }

            return(new Range(startPosition, endPosition));
        }
        /// <summary>
        ///     Inspect the specified position in the XML.
        /// </summary>
        /// <param name="absolutePosition">
        ///     The target position (0-based).
        /// </param>
        /// <returns>
        ///     An <see cref="SourceLocation"/> representing the result of the inspection.
        /// </returns>
        public SourceLocation Inspect(int absolutePosition)
        {
            if (absolutePosition < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(absolutePosition), absolutePosition, "Absolute position cannot be less than 0.");
            }

            return(Inspect(
                       _documentPositions.GetPosition(absolutePosition)
                       ));
        }
        public void GetAbsolutePosition_UnixLineEndings(int line, int column, char expectedChar)
        {
            const string text = TestData.TextWithUnixLineEndings.Text;

            TextPositions textPositions    = new TextPositions(text);
            int           absolutePosition = textPositions.GetAbsolutePosition(line, column);

            Assert.Equal(expectedChar, text[absolutePosition]);

            Position roundTripped = textPositions.GetPosition(absolutePosition).ToZeroBased();

            Assert.Equal(roundTripped.LineNumber, line);
            Assert.Equal(roundTripped.ColumnNumber, column);
        }
        public void GetPosition_WindowsLineEndings(char forChar, int expectedLine, int expectedColumn)
        {
            const string text = TestData.TextWithWindowsLineEndings.Text;

            int absolutePosition = text.IndexOf(forChar);

            Assert.InRange(absolutePosition, 0, text.Length - 1);

            TextPositions textPositions = new TextPositions(text);
            Position      position      = textPositions.GetPosition(absolutePosition);

            Assert.True(position.IsOneBased);
            Assert.Equal(expectedLine, position.LineNumber);
            Assert.Equal(expectedColumn, position.ColumnNumber);

            int absolutePositionRoundTripped = textPositions.GetAbsolutePosition(position);

            Assert.Equal(absolutePosition, absolutePositionRoundTripped);
        }
Example #6
0
            /// <summary>
            ///     Find the spaces between elements and use that to infer whitespace.
            /// </summary>
            void ComputeWhitespace()
            {
                // TODO: Merge contiguous whitespace.

                var discoveredElements = DiscoveredNodes.OfType <XSElementWithContent>()
                                         .OrderBy(discoveredNode => discoveredNode.Range.Start)
                                         .ThenBy(discoveredNode => discoveredNode.Range.End);

                foreach (XSElementWithContent element in discoveredElements)
                {
                    int          startOfNextNode, endOfNode, whitespaceLength;
                    XSWhitespace whitespace;

                    endOfNode = element.ElementNode.StartTag.Span.End;
                    for (int contentIndex = 0; contentIndex < element.Content.Count; contentIndex++)
                    {
                        if (element.Content[contentIndex] is XSElementText text)
                        {
                            startOfNextNode = _textPositions.GetAbsolutePosition(text.Range.Start);

                            whitespaceLength = startOfNextNode - endOfNode;
                            if (whitespaceLength > 0)
                            {
                                whitespace = new XSWhitespace(
                                    range: new Range(
                                        start: _textPositions.GetPosition(endOfNode),
                                        end: _textPositions.GetPosition(startOfNextNode)
                                        ),
                                    parent: element
                                    );
                                element.Content = element.Content.Insert(contentIndex, whitespace);
                                DiscoveredNodes.Add(whitespace);
                            }

                            endOfNode = _textPositions.GetAbsolutePosition(text.Range.End);
                        }

                        if (element.Content[contentIndex] is XSElement childElement)
                        {
                            startOfNextNode = childElement.ElementNode.Span.Start;

                            whitespaceLength = startOfNextNode - endOfNode;
                            if (whitespaceLength > 0)
                            {
                                whitespace = new XSWhitespace(
                                    range: new Range(
                                        start: _textPositions.GetPosition(endOfNode),
                                        end: _textPositions.GetPosition(startOfNextNode)
                                        ),
                                    parent: element
                                    );
                                element.Content = element.Content.Insert(contentIndex, whitespace);
                                DiscoveredNodes.Add(whitespace);
                            }

                            endOfNode = childElement.ElementNode.Span.End;
                        }
                    }

                    // Any trailing whitespace before the closing tag?
                    startOfNextNode  = element.ElementNode.EndTag.Span.Start;
                    whitespaceLength = startOfNextNode - endOfNode;
                    if (whitespaceLength > 0)
                    {
                        whitespace = new XSWhitespace(
                            range: new Range(
                                start: _textPositions.GetPosition(endOfNode),
                                end: _textPositions.GetPosition(startOfNextNode)
                                ),
                            parent: element
                            );
                        element.Content = element.Content.Add(whitespace);
                        DiscoveredNodes.Add(whitespace);
                    }
                }
            }