public override void VisitBlock(Block block)
        {
            if (CanRewrite(block))
            {
                SyntaxTreeNode newNode = RewriteBlock(_blocks.Peek(), block);
                if (newNode != null)
                {
                    _blocks.Peek().Children.Add(newNode);
                }
            }
            else
            {
                // Not rewritable.
                BlockBuilder builder = new BlockBuilder(block);
                builder.Children.Clear();
                _blocks.Push(builder);
                base.VisitBlock(block);
                Debug.Assert(ReferenceEquals(builder, _blocks.Peek()));

                if (_blocks.Count > 1)
                {
                    _blocks.Pop();
                    _blocks.Peek().Children.Add(builder.Build());
                }
            }
        }
		protected override SyntaxTreeNode RewriteSpan (BlockBuilder parent, Span span)
		{
			var b = new SpanBuilder (span);
			var old = (LiteralAttributeCodeGenerator)span.CodeGenerator;
			b.CodeGenerator = old.ValueGenerator != null
				? new PreprocessedLiteralAttributeCodeGenerator (old.Prefix, old.ValueGenerator)
				: new PreprocessedLiteralAttributeCodeGenerator (old.Prefix, old.Value);
			return b.Build ();
		}
示例#3
0
        private void MinifyMarkup(BlockBuilder block)
        {
            var codeGenerator = new MarkupCodeGenerator();
            var previousIsWhiteSpace = true;
            var previousTokenEndsWithBlockElement = true;
            var insideScript = false;

            for (int i = 0; i < block.Children.Count; i++)
            {
                var node = block.Children[i];
                var span = node as Span;
                if (span == null)
                {
                    // When we have a dynamic markup, we can't know if the last char will be whitespace
                    // => to make it work in all cases, we won't minifiy whitespace just after code.
                    previousIsWhiteSpace = false;
                    previousTokenEndsWithBlockElement = false;
                    continue;
                }

                // There may be several HTML tokens just one after the other.
                // => we concatenate everything in a single token to minify everyting in a single scan
                // (we is better for Javascript minification).
                var sb = new StringBuilder();
                sb.Append(span.Content);
                if (i < block.Children.Count - 1)
                {
                    var markup = block.Children[i + 1] as Span;
                    while ((markup != null) && (markup.Kind == SpanKind.Markup) && ((markup.Next == null) || ((markup.Next != null) && ((markup.Next.Kind == SpanKind.Markup) || ((markup.Next.Kind != SpanKind.Markup) && !markup.Content.EndsWith("\""))))))
                    {
                        block.Children.RemoveAt(i + 1);
                        sb.Append(markup.Content);
                        markup = i + 1 < block.Children.Count ? block.Children[i + 1] as Span : null;
                    }
                }

                var content = sb.ToString();
                if (string.IsNullOrEmpty(content))
                {
                    // Nothing to minify
                    block.Children.RemoveAt(i);
                    continue;
                }

                content = _minifier.Minify(content, previousIsWhiteSpace, previousTokenEndsWithBlockElement, insideScript);

                _minifier.AnalyseContent(content, ref previousIsWhiteSpace, ref previousTokenEndsWithBlockElement, ref insideScript);

                // We replace the content with the minified markup
                // and then let the CSharp/VB generator do their jobs.
                var builder = new SpanBuilder() { CodeGenerator = codeGenerator, EditHandler = span.EditHandler, Kind = span.Kind, Start = span.Start };
                var symbol = new MarkupSymbol() { Content = content };
                builder.Accept(symbol);
                span.ReplaceWith(builder);
            }
        }
        protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block)
        {
            // Collect the content of this node
            string content = String.Concat(block.Children.Cast<Span>().Select(s => s.Content));

            // Create a new span containing this content
            SpanBuilder span = new SpanBuilder();
            FillSpan(span, block.Children.Cast<Span>().First().Start, content);
            return span.Build();
        }
		protected override SyntaxTreeNode RewriteBlock (BlockBuilder parent, Block block)
		{
			var b = new BlockBuilder (block);
			var abGen = block.CodeGenerator as AttributeBlockCodeGenerator;
			if (abGen != null) {
				b.CodeGenerator = new PreprocessedAttributeBlockCodeGenerator (abGen);
			} else {
				b.CodeGenerator = new PreprocessedDynamicAttributeBlockCodeGenerator ((DynamicAttributeBlockCodeGenerator)b.CodeGenerator);
			}
			return b.Build ();
		}
        public void VisitSendsDocumentToVisitor()
        {
            // Arrange
            Mock<ParserVisitor> targetMock = new Mock<ParserVisitor>();
            Block root = new BlockBuilder() { Type = BlockType.Comment }.Build();
            ParserResults results = new ParserResults(root, new List<RazorError>());

            // Act
            targetMock.Object.Visit(results);

            // Assert
            targetMock.Verify(v => v.VisitBlock(root));
        }
示例#7
0
        public void ConstructorWithBlockBuilderSetsParent()
        {
            // Arrange
            BlockBuilder builder = new BlockBuilder() { Type = BlockType.Comment };
            Span span = new SpanBuilder() { Kind = SpanKind.Code }.Build();
            builder.Children.Add(span);

            // Act
            Block block = builder.Build();

            // Assert
            Assert.Same(block, span.Parent);
        }
        protected override SyntaxTreeNode RewriteSpan(BlockBuilder parent, Span span)
        {
            // Only rewrite if we have a previous that is also markup (CanRewrite does this check for us!)
            Span previous = parent.Children.LastOrDefault() as Span;
            if (previous == null || !CanRewrite(previous))
            {
                return span;
            }

            // Merge spans
            parent.Children.Remove(previous);
            SpanBuilder merged = new SpanBuilder();
            FillSpan(merged, previous.Start, previous.Content + span.Content);
            return merged.Build();
        }
示例#9
0
        public void ConstructorTransfersInstanceOfCodeGeneratorFromBlockBuilder()
        {
            // Arrange
            IBlockCodeGenerator expected = new ExpressionCodeGenerator();
            BlockBuilder builder = new BlockBuilder()
            {
                Type = BlockType.Helper,
                CodeGenerator = expected
            };

            // Act
            Block actual = builder.Build();

            // Assert
            Assert.Same(expected, actual.CodeGenerator);
        }
示例#10
0
        public void ConstructorCopiesBasicValuesFromBlockBuilder()
        {
            // Arrange
            BlockBuilder builder = new BlockBuilder()
            {
                Name = "Foo",
                Type = BlockType.Helper
            };

            // Act
            Block actual = builder.Build();

            // Assert
            Assert.Equal("Foo", actual.Name);
            Assert.Equal(BlockType.Helper, actual.Type);
        }
示例#11
0
        public void ConstructorTransfersChildrenFromBlockBuilder()
        {
            // Arrange
            Span expected = new SpanBuilder() { Kind = SpanKind.Code }.Build();
            BlockBuilder builder = new BlockBuilder()
            {
                Type = BlockType.Functions
            };
            builder.Children.Add(expected);

            // Act
            Block block = builder.Build();

            // Assert
            Assert.Same(expected, block.Children.Single());
        }
示例#12
0
        public Block(BlockBuilder source)
        {
            if (source.Type == null)
            {
                throw new InvalidOperationException(RazorResources.Block_Type_Not_Specified);
            }
            Type = source.Type.Value;
            Children = source.Children;
            Name = source.Name;
            CodeGenerator = source.CodeGenerator;
            source.Reset();

            foreach (SyntaxTreeNode node in Children)
            {
                node.Parent = this;
            }
        }
        public void VisitCallsOnCompleteWhenAllNodesHaveBeenVisited()
        {
            // Arrange
            Mock<ParserVisitor> targetMock = new Mock<ParserVisitor>();
            Block root = new BlockBuilder() { Type = BlockType.Comment }.Build();
            List<RazorError> errors = new List<RazorError>() {
                new RazorError("Foo", 1, 0, 1),
                new RazorError("Bar", 2, 0, 2)
            };
            ParserResults results = new ParserResults(root, errors);

            // Act
            targetMock.Object.Visit(results);

            // Assert
            targetMock.Verify(v => v.OnComplete());
        }
        public void VisitSendsErrorsToVisitor()
        {
            // Arrange
            Mock<ParserVisitor> targetMock = new Mock<ParserVisitor>();
            Block root = new BlockBuilder() { Type = BlockType.Comment }.Build();
            List<RazorError> errors = new List<RazorError>() {
                new RazorError("Foo", 1, 0, 1),
                new RazorError("Bar", 2, 0, 2)
            };
            ParserResults results = new ParserResults(root, errors);

            // Act
            targetMock.Object.Visit(results);

            // Assert
            targetMock.Verify(v => v.VisitError(errors[0]));
            targetMock.Verify(v => v.VisitError(errors[1]));
        }
示例#15
0
        /// <summary>
        /// The HTML attributes are parsed in special blocks to generate the attributes only when they
        /// have a value.
        /// => To enhance the minification, we put all the hard coded attributes back to the stream of HTML.
        /// </summary>
        /// <param name="block"></param>
        private static void FlattenHTMLAttributes(BlockBuilder block)
        {
            for (int i = 0; i < block.Children.Count; )
            {
                var node = block.Children[i];
                var subblock = node as Block;
                if ((subblock == null) || (!subblock.Children.All(c => c is Span && ((Span)c).Kind == SpanKind.Markup)))
                {
                    i++;
                    continue;
                }

                block.Children.RemoveAt(i);
                foreach (var child in subblock.Children)
                {
                    block.Children.Insert(i++, child);
                }
            }
        }
        //public override void VisitBlock(Block block)
        //{
        //    BlockBuilder parent = null;
        //    if (_blocks.Count > 0)
        //    {
        //        parent = _blocks.Peek();
        //    }
        //    BlockBuilder newBlock = new BlockBuilder(block);
        //    newBlock.Children.Clear();
        //    _blocks.Push(newBlock);
        //    if (block.Type == BlockType.Expression && parent != null)
        //    {
        //        VisitExpressionBlock(block, parent);
        //    }
        //    else
        //    {
        //        base.VisitBlock(block);
        //    }
        //    if (_blocks.Count > 1)
        //    {
        //        parent.Children.Add(_blocks.Pop().Build());
        //    }
        //}

        //public override void VisitSpan(Span span)
        //{
        //    Debug.Assert(_blocks.Count > 0);
        //    _blocks.Peek().Children.Add(span);
        //}

        protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block)
        {
            BlockBuilder newBlock = new BlockBuilder(block);
            newBlock.Children.Clear();
            Span ws = block.Children.FirstOrDefault() as Span;
            IEnumerable<SyntaxTreeNode> newNodes = block.Children;
            if (ws.Content.All(Char.IsWhiteSpace))
            {
                // Add this node to the parent
                SpanBuilder builder = new SpanBuilder(ws);
                builder.ClearSymbols();
                FillSpan(builder, ws.Start, ws.Content);
                parent.Children.Add(builder.Build());

                // Remove the old whitespace node
                newNodes = block.Children.Skip(1);
            }

            foreach (SyntaxTreeNode node in newNodes)
            {
                newBlock.Children.Add(node);
            }
            return newBlock.Build();
        }
 protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedErrors)
 {
     BlockBuilder builder = new BlockBuilder();
     builder.Type = blockType;
     ParseBlockTest(
         document,
         ConfigureAndAddSpanToBlock(
             builder,
             Factory.Span(spanType, spanContent, spanType == SpanKind.Markup)
                    .Accepts(acceptedCharacters)),
         expectedErrors ?? new RazorError[0]);
 }
        public void AddSpanAddsSpanToCurrentBlockBuilder()
        {
            // Arrange
            var factory = SpanFactory.CreateCsHtml();
            Mock<ParserVisitor> mockListener = new Mock<ParserVisitor>();
            ParserContext context = SetupTestContext("phoo");

            SpanBuilder builder = new SpanBuilder()
            {
                Kind = SpanKind.Code
            };
            builder.Accept(new CSharpSymbol(1, 0, 1, "foo", CSharpSymbolType.Identifier));
            Span added = builder.Build();

            using (context.StartBlock(BlockType.Functions))
            {
                context.AddSpan(added);
            }

            BlockBuilder expected = new BlockBuilder()
            {
                Type = BlockType.Functions,
            };
            expected.Children.Add(added);

            // Assert
            ParserTestBase.EvaluateResults(context.CompleteParse(), expected.Build());
        }
示例#19
0
 protected virtual SyntaxTreeNode RewriteSpan(BlockBuilder parent, Span span)
 {
     throw new NotImplementedException();
 }
 protected virtual Block CreateSimpleBlockAndSpan(string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
 {
     SpanConstructor span = Factory.Span(spanType, spanContent, spanType == SpanKind.Markup).Accepts(acceptedCharacters);
     BlockBuilder b = new BlockBuilder()
     {
         Type = blockType
     };
     return ConfigureAndAddSpanToBlock(b, span);
 }
示例#21
0
 protected virtual SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block)
 {
     throw new NotImplementedException();
 }
 protected virtual Block ConfigureAndAddSpanToBlock(BlockBuilder block, SpanConstructor span)
 {
     switch (block.Type)
     {
         case BlockType.Markup:
             span.With(new MarkupCodeGenerator());
             break;
         case BlockType.Statement:
             span.With(new StatementCodeGenerator());
             break;
         case BlockType.Expression:
             block.CodeGenerator = new ExpressionCodeGenerator();
             span.With(new ExpressionCodeGenerator());
             break;
     }
     block.Children.Add(span);
     return block.Build();
 }
示例#23
0
        private void UnterminatedBlockCore(string keyword, string expectedTerminator, BlockType blockType, bool keywordIsMetaCode, Func<UnclassifiedCodeSpanConstructor, SpanConstructor> classifier)
        {
            const string blockBody = @"
    ' This block is not correctly terminated!";

            BlockBuilder expected = new BlockBuilder();
            expected.Type = blockType;
            if (keywordIsMetaCode)
            {
                expected.Children.Add(Factory.MetaCode(keyword).Accepts(AcceptedCharacters.None));
                expected.Children.Add(classifier(Factory.Code(blockBody)));
            }
            else
            {
                expected.Children.Add(classifier(Factory.Code(keyword + blockBody)));
            }

            ParseBlockTest(keyword + blockBody,
                expected.Build(),
                new RazorError(
                    String.Format(RazorResources.ParseError_BlockNotTerminated, keyword, expectedTerminator),
                    SourceLocation.Zero));
        }
示例#24
0
        private void EofBlockCore(string keyword, string expectedTerminator, bool autoComplete, BlockType blockType, bool keywordIsMetaCode, Func<UnclassifiedCodeSpanConstructor, SpanConstructor> classifier)
        {
            BlockBuilder expected = new BlockBuilder();
            expected.Type = blockType;
            if (keywordIsMetaCode)
            {
                expected.Children.Add(Factory.MetaCode(keyword).Accepts(AcceptedCharacters.None));
                expected.Children.Add(
                classifier(Factory.EmptyVB())
                       .With((SpanEditHandler)(
                            autoComplete ?
                                new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = expectedTerminator } :
                                SpanEditHandler.CreateDefault())));
            }
            else
            {
                expected.Children.Add(
                    classifier(Factory.Code(keyword))
                           .With((SpanEditHandler)(
                                autoComplete ?
                                    new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = expectedTerminator } :
                                    SpanEditHandler.CreateDefault())));
            }

            ParseBlockTest(keyword,
                expected.Build(),
                new RazorError(
                    String.Format(RazorResources.ParseError_BlockNotTerminated, keyword, expectedTerminator),
                    SourceLocation.Zero));
        }
示例#25
0
        private Block MinifySectionBlock(Block block)
        {
            var builder = new BlockBuilder(block);

            // In sections, we only change the Markup blocks
            // as the others handle the section integration in the calling page.
            for (int i = 0; i < builder.Children.Count; i++)
            {
                var node = builder.Children[i];
                var markup = node as Block;
                if ((markup == null) || (markup.Type != BlockType.Markup))
                {
                    continue;
                }

                var markupbuilder = new BlockBuilder(markup);
                FlattenHTMLAttributes(markupbuilder);
                MinifyMarkup(markupbuilder);
                markup = new Block(markupbuilder);
                builder.Children[i] = markup;
            }

            block = new Block(builder);
            return block;
        }