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 (); }
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)); }
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(); }
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); }
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); }
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()); }
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])); }
/// <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()); }
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); }
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(); }
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)); }
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)); }
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; }