public override void BuildSpan(SpanBuilder span, SourceLocation start, string content)
 {
     foreach (IToken sym in Language.TokenizeString(start, content))
     {
         span.Accept(sym);
     }
 }
Example #2
0
        public void ReplaceWith_NotifiesParentChildHasChanged()
        {
            // Arrange
            var spanBuilder = new SpanBuilder(SourceLocation.Zero);

            spanBuilder.Accept(SyntaxFactory.Token(SyntaxKind.HtmlTextLiteral, "hello"));
            var span         = spanBuilder.Build();
            var blockBuilder = new BlockBuilder()
            {
                Type = BlockKindInternal.Markup,
            };

            blockBuilder.Children.Add(span);
            var block = blockBuilder.Build();

            span.Parent = block;
            var originalBlockLength = block.Length;
            var newSpanBuilder      = new SpanBuilder(SourceLocation.Zero);

            newSpanBuilder.Accept(SyntaxFactory.Token(SyntaxKind.HtmlTextLiteral, "hi"));

            // Act
            span.ReplaceWith(newSpanBuilder);

            // Assert
            Assert.Equal(5, originalBlockLength);
            Assert.Equal(2, block.Length);
        }
Example #3
0
        protected void FillSpan(SpanBuilder builder, SourceLocation start, string content)
        {
            builder.Kind           = SpanKindInternal.Markup;
            builder.ChunkGenerator = new MarkupChunkGenerator();

            foreach (IToken sym in HtmlLanguageCharacteristics.Instance.TokenizeString(start, content))
            {
                builder.Accept(sym);
            }
        }
Example #4
0
 public SpanConstructor(SpanKindInternal kind, SourceLocation location, IEnumerable <IToken> tokens)
 {
     Builder             = new SpanBuilder(location);
     Builder.Kind        = kind;
     Builder.EditHandler = SpanEditHandler.CreateDefault((content) => SpanConstructor.TestTokenizer(content));
     foreach (IToken sym in tokens)
     {
         Builder.Accept(sym);
     }
 }
        private static Span GetSpan(SourceLocation start, string content)
        {
            var spanBuilder = new SpanBuilder(start);
            var tokens      = CSharpLanguageCharacteristics.Instance.TokenizeString(content).ToArray();

            foreach (var token in tokens)
            {
                spanBuilder.Accept(token);
            }
            var span = spanBuilder.Build();

            return(span);
        }
Example #6
0
        protected virtual SpanBuilder UpdateSpan(Span target, SourceChange change)
        {
            var newContent = change.GetEditedContent(target);
            var newSpan    = new SpanBuilder(target);

            newSpan.ClearTokens();
            foreach (var token in Tokenizer(newContent))
            {
                newSpan.Accept(token);
            }
            if (target.Next != null)
            {
                var newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent);
                target.Next.ChangeStart(newEnd);
            }
            return(newSpan);
        }
Example #7
0
        public void ReplaceWith_ResetsLength()
        {
            // Arrange
            var builder = new SpanBuilder(SourceLocation.Zero);

            builder.Accept(SyntaxFactory.Token(SyntaxKind.HtmlTextLiteral, "hello"));
            var span       = builder.Build();
            var newBuilder = new SpanBuilder(SourceLocation.Zero);

            newBuilder.Accept(SyntaxFactory.Token(SyntaxKind.HtmlTextLiteral, "hi"));
            var originalLength = span.Length;

            // Act
            span.ReplaceWith(newBuilder);

            // Assert
            Assert.Equal(5, originalLength);
            Assert.Equal(2, span.Length);
        }
Example #8
0
        public void ReplaceWith_ResetsLength()
        {
            // Arrange
            var builder = new SpanBuilder(SourceLocation.Zero);

            builder.Accept(new HtmlToken("hello", HtmlTokenType.Text));
            var span       = builder.Build();
            var newBuilder = new SpanBuilder(SourceLocation.Zero);

            newBuilder.Accept(new HtmlToken("hi", HtmlTokenType.Text));
            var originalLength = span.Length;

            // Act
            span.ReplaceWith(newBuilder);

            // Assert
            Assert.Equal(5, originalLength);
            Assert.Equal(2, span.Length);
        }
Example #9
0
        public void Clone_ClonesSpan()
        {
            // Arrange
            var spanBuilder = new SpanBuilder(new SourceLocation(1, 2, 3))
            {
                EditHandler    = new SpanEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString),
                Kind           = SpanKindInternal.Transition,
                ChunkGenerator = new ExpressionChunkGenerator(),
            };

            spanBuilder.Accept(new CSharpSymbol("@", CSharpSymbolType.Transition));
            var span = spanBuilder.Build();

            // Act
            var copy = (Span)span.Clone();

            // Assert
            Assert.Equal(span, copy);
            Assert.NotSame(span, copy);
        }
Example #10
0
        public void ChildChanged_NotifiesParent()
        {
            // Arrange
            var spanBuilder = new SpanBuilder(SourceLocation.Zero);

            spanBuilder.Accept(new HtmlSymbol("hello", HtmlSymbolType.Text));
            var span         = spanBuilder.Build();
            var blockBuilder = new BlockBuilder()
            {
                Type = BlockKindInternal.Markup,
            };

            blockBuilder.Children.Add(span);
            var childBlock = blockBuilder.Build();

            blockBuilder = new BlockBuilder()
            {
                Type = BlockKindInternal.Markup,
            };
            blockBuilder.Children.Add(childBlock);
            var parentBlock         = blockBuilder.Build();
            var originalBlockLength = parentBlock.Length;

            spanBuilder = new SpanBuilder(SourceLocation.Zero);
            spanBuilder.Accept(new HtmlSymbol("hi", HtmlSymbolType.Text));
            span.ReplaceWith(spanBuilder);

            // Wire up parents now so we can re-trigger ChildChanged to cause cache refresh.
            span.Parent       = childBlock;
            childBlock.Parent = parentBlock;

            // Act
            childBlock.ChildChanged();

            // Assert
            Assert.Equal(5, originalBlockLength);
            Assert.Equal(2, parentBlock.Length);
        }
Example #11
0
        // This method handles cases when the attribute is a simple span attribute such as
        // class="something moresomething".  This does not handle complex attributes such as
        // class="@myclass". Therefore the span.Content is equivalent to the entire attribute.
        private static TryParseResult TryParseSpan(
            Span span,
            IEnumerable <TagHelperDescriptor> descriptors,
            ErrorSink errorSink,
            HashSet <string> processedBoundAttributeNames)
        {
            var afterEquals = false;
            var builder     = new SpanBuilder(span.Start)
            {
                ChunkGenerator = span.ChunkGenerator,
                EditHandler    = span.EditHandler,
                Kind           = span.Kind
            };

            // Will contain symbols that represent a single attribute value: <input| class="btn"| />
            var htmlSymbols = span.Symbols.OfType <HtmlSymbol>().ToArray();
            var capturedAttributeValueStart = false;
            var attributeValueStartLocation = span.Start;

            // Default to DoubleQuotes. We purposefully do not persist NoQuotes ValueStyle to stay consistent with the
            // TryParseBlock() variation of attribute parsing.
            var attributeValueStyle = AttributeStructure.DoubleQuotes;

            // The symbolOffset is initialized to 0 to expect worst case: "class=". If a quote is found later on for
            // the attribute value the symbolOffset is adjusted accordingly.
            var    symbolOffset = 0;
            string name         = null;

            // Iterate down through the symbols to find the name and the start of the value.
            // We subtract the symbolOffset so we don't accept an ending quote of a span.
            for (var i = 0; i < htmlSymbols.Length - symbolOffset; i++)
            {
                var symbol = htmlSymbols[i];

                if (afterEquals)
                {
                    // We've captured all leading whitespace, the attribute name, and an equals with an optional
                    // quote/double quote. We're now at: " asp-for='|...'" or " asp-for=|..."
                    // The goal here is to capture all symbols until the end of the attribute. Note this will not
                    // consume an ending quote due to the symbolOffset.

                    // When symbols are accepted into SpanBuilders, their locations get altered to be offset by the
                    // parent which is why we need to mark our start location prior to adding the symbol.
                    // This is needed to know the location of the attribute value start within the document.
                    if (!capturedAttributeValueStart)
                    {
                        capturedAttributeValueStart = true;

                        attributeValueStartLocation = symbol.Start;
                    }

                    builder.Accept(symbol);
                }
                else if (name == null && HtmlMarkupParser.IsValidAttributeNameSymbol(symbol))
                {
                    // We've captured all leading whitespace prior to the attribute name.
                    // We're now at: " |asp-for='...'" or " |asp-for=..."
                    // The goal here is to capture the attribute name.

                    var nameBuilder = new StringBuilder();
                    // Move the indexer past the attribute name symbols.
                    for (var j = i; j < htmlSymbols.Length; j++)
                    {
                        var nameSymbol = htmlSymbols[j];
                        if (!HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
                        {
                            break;
                        }

                        nameBuilder.Append(nameSymbol.Content);
                        i++;
                    }

                    i--;

                    name = nameBuilder.ToString();
                    attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, name);
                }
                else if (symbol.Type == HtmlSymbolType.Equals)
                {
                    // We've captured all leading whitespace and the attribute name.
                    // We're now at: " asp-for|='...'" or " asp-for|=..."
                    // The goal here is to consume the equal sign and the optional single/double-quote.

                    // The coming symbols will either be a quote or value (in the case that the value is unquoted).

                    SourceLocation symbolStartLocation;

                    // Skip the whitespace preceding the start of the attribute value.
                    do
                    {
                        i++; // Start from the symbol after '='.
                    } while (i < htmlSymbols.Length &&
                             (htmlSymbols[i].Type == HtmlSymbolType.WhiteSpace ||
                              htmlSymbols[i].Type == HtmlSymbolType.NewLine));

                    // Check for attribute start values, aka single or double quote
                    if (i < htmlSymbols.Length && IsQuote(htmlSymbols[i]))
                    {
                        if (htmlSymbols[i].Type == HtmlSymbolType.SingleQuote)
                        {
                            attributeValueStyle = AttributeStructure.SingleQuotes;
                        }

                        symbolStartLocation = htmlSymbols[i].Start;

                        // If there's a start quote then there must be an end quote to be valid, skip it.
                        symbolOffset = 1;
                    }
                    else
                    {
                        // We are at the symbol after equals. Go back to equals to ensure we don't skip past that symbol.
                        i--;

                        symbolStartLocation = symbol.Start;
                    }

                    attributeValueStartLocation = new SourceLocation(
                        symbolStartLocation.FilePath,
                        symbolStartLocation.AbsoluteIndex + 1,
                        symbolStartLocation.LineIndex,
                        symbolStartLocation.CharacterIndex + 1);

                    afterEquals = true;
                }
                else if (symbol.Type == HtmlSymbolType.WhiteSpace)
                {
                    // We're at the start of the attribute, this branch may be hit on the first iterations of
                    // the loop since the parser separates attributes with their spaces included as symbols.
                    // We're at: "| asp-for='...'" or "| asp-for=..."
                    // Note: This will not be hit even for situations like asp-for  ="..." because the core Razor
                    // parser currently does not know how to handle attributes in that format. This will be addressed
                    // by https://github.com/aspnet/Razor/issues/123.

                    attributeValueStartLocation = SourceLocationTracker.Advance(attributeValueStartLocation, symbol.Content);
                }
            }

            // After all symbols have been added we need to set the builders start position so we do not indirectly
            // modify the span's start location.
            builder.Start = attributeValueStartLocation;

            if (name == null)
            {
                // We couldn't find a name, if the original span content was whitespace it ultimately means the tag
                // that owns this "attribute" is malformed and is expecting a user to type a new attribute.
                // ex: <myTH class="btn"| |
                if (!string.IsNullOrWhiteSpace(span.Content))
                {
                    errorSink.OnError(
                        span.Start,
                        LegacyResources.TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed,
                        span.Content.Length);
                }

                return(null);
            }

            var result = CreateTryParseResult(name, descriptors, processedBoundAttributeNames);

            // If we're not after an equal then we should treat the value as if it were a minimized attribute.
            Span attributeValue = null;

            if (afterEquals)
            {
                attributeValue = CreateMarkupAttribute(builder, result);
            }
            else
            {
                attributeValueStyle = AttributeStructure.Minimized;
            }

            result.AttributeValueNode = attributeValue;
            result.AttributeStructure = attributeValueStyle;
            return(result);
        }