public void TestMultipleAttenuation() { RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); KeyPair root = new KeyPair(rng); SymbolTable symbols = Biscuit.Token.Biscuit.DefaultSymbolTable(); BlockBuilder authority_builder = new BlockBuilder(0, symbols); DateTime date = DateTime.Now; authority_builder.AddFact(Utils.Fact("revocation_id", Arrays.AsList(Utils.Date(date)))); Biscuit.Token.Biscuit biscuit = Biscuit.Token.Biscuit.Make(rng, root, Biscuit.Token.Biscuit.DefaultSymbolTable(), authority_builder.Build()).Right; BlockBuilder builder = biscuit.CreateBlock(); builder.AddFact(Utils.Fact( "right", Arrays.AsList(Utils.Symbol("topic"), Utils.Symbol("tenant"), Utils.Symbol("namespace"), Utils.Symbol("topic"), Utils.Symbol("produce")) )); string attenuatedB64 = biscuit.Attenuate(rng, new KeyPair(rng), builder.Build()).Right.SerializeBase64().Right; Console.WriteLine("attenuated: " + attenuatedB64); var attenuatedB64Biscuit = Biscuit.Token.Biscuit.FromBase64(attenuatedB64); Assert.IsTrue(attenuatedB64Biscuit.IsRight); string attenuated2B64 = biscuit.Attenuate(rng, new KeyPair(rng), builder.Build()).Right.SerializeBase64().Right; Console.WriteLine("attenuated2: " + attenuated2B64); var attenuated2B64Biscuit = Biscuit.Token.Biscuit.FromBase64(attenuated2B64); Assert.IsTrue(attenuated2B64Biscuit.IsRight); }
protected internal virtual GeneratorResults GenerateCodeCore( ITextDocument input, string className, string rootNamespace, string sourceFileName, string checksum, CancellationToken?cancelToken) { if (input == null) { throw new ArgumentNullException(nameof(input)); } try { className = (className ?? Host.DefaultClassName) ?? DefaultClassName; rootNamespace = (rootNamespace ?? Host.DefaultNamespace) ?? DefaultNamespace; // Run the parser var parser = CreateParser(sourceFileName); Debug.Assert(parser != null); var results = parser.Parse(input); // Generate code var chunkGenerator = CreateChunkGenerator(className, rootNamespace, sourceFileName); chunkGenerator.DesignTimeMode = Host.DesignTimeMode; chunkGenerator.Visit(results); var codeGeneratorContext = new CodeGeneratorContext(chunkGenerator.Context, results.ErrorSink); codeGeneratorContext.Checksum = checksum; var codeGenerator = CreateCodeGenerator(codeGeneratorContext); var codeGeneratorResult = codeGenerator.Generate(); // Collect results and return return(new GeneratorResults(results, codeGeneratorResult, codeGeneratorContext.ChunkTreeBuilder.Root)); } // During runtime we want code generation explosions to flow up into the calling code. At design time // we want to capture these exceptions to prevent IDEs from crashing. catch (Exception ex) when(Host.DesignTimeMode) { var errorSink = new ErrorSink(); errorSink.OnError( SourceLocation.Undefined, RazorResources.FormatFatalException(sourceFileName, Environment.NewLine, ex.Message), length: -1); var emptyBlock = new BlockBuilder(); emptyBlock.Type = default(BlockType); return(new GeneratorResults( document: emptyBlock.Build(), tagHelperDescriptors: Enumerable.Empty <TagHelperDescriptor>(), errorSink: errorSink, codeGeneratorResult: new CodeGeneratorResult( code: string.Empty, designTimeLineMappings: new List <LineMapping>()), chunkTree: new ChunkTree())); } }
public RazorSyntaxTree Execute(RazorCodeDocument document, RazorSyntaxTree syntaxTree) { var trees = GetImportedSyntaxTrees(document); if (trees.Count == 0) { return(syntaxTree); } var errors = new List <RazorError>(syntaxTree.Diagnostics); var blockBuilder = new BlockBuilder(syntaxTree.Root); for (var i = 0; i < trees.Count; i++) { var tree = trees[i]; blockBuilder.Children.Insert(i, tree.Root); errors.AddRange(tree.Diagnostics); foreach (var error in tree.Diagnostics) { document.ErrorSink.OnError(error); } } return(RazorSyntaxTree.Create(blockBuilder.Build(), errors)); }
public override void VisitBlock(Block block) { if (CanRewrite(block)) { SyntaxTreeNode newNode = RewriteBlock(_blocks.Peek(), block); block = newNode as Block; if (block == null) { _blocks.Peek().Children.Add(newNode); return; } } 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()); } }
public void AddSpanAddsSpanToCurrentBlockBuilder() { // Arrange var factory = SpanFactory.CreateCsHtml(); Mock <ParserVisitor> mockListener = new Mock <ParserVisitor>(); var context = SetupTestContext("phoo"); var builder = new SpanBuilder() { Kind = SpanKind.Code }; builder.Accept(new CSharpSymbol(1, 0, 1, "foo", CSharpSymbolType.Identifier)); var added = builder.Build(); using (context.StartBlock(BlockType.Functions)) { context.AddSpan(added); } var expected = new BlockBuilder() { Type = BlockType.Functions, }; expected.Children.Add(added); // Assert ParserTestBase.EvaluateResults(context.CompleteParse(), expected.Build()); }
public void TestGetRevocationIds() { RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); KeyPair root = new KeyPair(rng); SymbolTable symbols = Biscuit.Token.Biscuit.DefaultSymbolTable(); BlockBuilder authority_builder = new BlockBuilder(0, symbols); Guid uuid1 = Guid.Parse("0b6d033d-83da-437f-a078-1a44890018bc"); authority_builder.AddFact(Utils.Fact("revocation_id", Arrays.AsList(Utils.Strings(uuid1.ToString())))); Biscuit.Token.Biscuit biscuit = Biscuit.Token.Biscuit.Make(rng, root, Biscuit.Token.Biscuit.DefaultSymbolTable(), authority_builder.Build()).Right; BlockBuilder builder = biscuit.CreateBlock(); builder.AddFact(Utils.Fact( "right", Arrays.AsList(Utils.Symbol("topic"), Utils.Symbol("tenant"), Utils.Symbol("namespace"), Utils.Symbol("topic"), Utils.Symbol("produce")) )); Guid uuid2 = Guid.Parse("46a103de-ee65-4d04-936b-9111eac7dd3b"); builder.AddFact(Utils.Fact("revocation_id", Arrays.AsList(Utils.Strings(uuid2.ToString())))); string attenuatedB64 = biscuit.Attenuate(rng, new KeyPair(rng), builder.Build()).Right.SerializeBase64().Right; Biscuit.Token.Biscuit b = Biscuit.Token.Biscuit.FromBase64(attenuatedB64).Right; Verifier v1 = b.Verify(root.ToPublicKey()).Right; List <Guid> revokedIds = v1.GetRevocationIdentifiers().Right.Select(s => Guid.Parse(s)).ToList(); Assert.IsTrue(revokedIds.Contains(uuid1)); Assert.IsTrue(revokedIds.Contains(uuid2)); }
public override void VisitBlock(Block block) { if (CanRewrite(block)) { var newNode = RewriteBlock(_blocks.Peek(), block); if (newNode != null) { _blocks.Peek().Children.Add(newNode); } } else { // Not rewritable. var 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()); } } }
private static Block ConvertToMarkupAttributeBlock( Block block, Func <Block, Span, Span> createMarkupAttribute) { var blockBuilder = new BlockBuilder { ChunkGenerator = block.ChunkGenerator, Type = block.Type }; foreach (var child in block.Children) { SyntaxTreeNode markupAttributeChild; if (child.IsBlock) { markupAttributeChild = ConvertToMarkupAttributeBlock((Block)child, createMarkupAttribute); } else { markupAttributeChild = createMarkupAttribute(block, (Span)child); } blockBuilder.Children.Add(markupAttributeChild); } return(blockBuilder.Build()); }
private static Block ConvertToMarkupAttributeBlock(Block block, bool isBoundNonStringAttribute) { var blockBuilder = new BlockBuilder { ChunkGenerator = block.ChunkGenerator, Type = block.Type }; foreach (var child in block.Children) { SyntaxTreeNode markupAttributeChild; if (child.IsBlock) { markupAttributeChild = ConvertToMarkupAttributeBlock((Block)child, isBoundNonStringAttribute); } else { var spanBuilder = new SpanBuilder((Span)child); markupAttributeChild = CreateMarkupAttribute(spanBuilder, isBoundNonStringAttribute); } blockBuilder.Children.Add(markupAttributeChild); } return(blockBuilder.Build()); }
//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()); }
public void Rewrite(RewritingContext context) { RewriteTags(context.SyntaxTree); ValidateRewrittenSyntaxTree(context); context.SyntaxTree = _currentBlock.Build(); }
public static Block BuildEmptyBlock() { var builder = new BlockBuilder(); builder.Name = "Test"; builder.Type = BlockType.Comment; return(builder.Build()); }
private static KeyValuePair <string, SyntaxTreeNode> ParseBlock(Block block) { // TODO: Accept more than just spans: https://github.com/aspnet/Razor/issues/96. // The first child will only ever NOT be a Span if a user is doing something like: // <input @checked /> var childSpan = block.Children.First() as Span; if (childSpan == null) { throw new InvalidOperationException(RazorResources.TagHelpers_CannotHaveCSharpInTagDeclaration); } var builder = new BlockBuilder(block); // If there's only 1 child it means that it's plain text inside of the attribute. // i.e. <div class="plain text in attribute"> if (builder.Children.Count == 1) { return(ParseSpan(childSpan)); } var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.Text); var name = textSymbol != null ? textSymbol.Content : null; if (name == null) { throw new InvalidOperationException(RazorResources.TagHelpers_AttributesMustHaveAName); } // Remove first child i.e. foo=" builder.Children.RemoveAt(0); // Grabbing last child to check if the attribute value is quoted. var endNode = block.Children.Last(); if (!endNode.IsBlock) { var endSpan = (Span)endNode; var endSymbol = (HtmlSymbol)endSpan.Symbols.Last(); // Checking to see if it's a quoted attribute, if so we should remove end quote if (IsQuote(endSymbol)) { builder.Children.RemoveAt(builder.Children.Count - 1); } } // We need to rebuild the code generators of the builder and its children (this is needed to // ensure we don't do special attribute code generation since this is a tag helper). block = RebuildCodeGenerators(builder.Build()); return(new KeyValuePair <string, SyntaxTreeNode>(name, block)); }
public void ConstructorWithBlockBuilderSetsParent() { // Arrange var builder = new BlockBuilder() { Type = BlockType.Comment }; var span = new SpanBuilder() { Kind = SpanKind.Code }.Build(); builder.Children.Add(span); // Act var block = builder.Build(); // Assert Assert.Same(block, span.Parent); }
public static Block BuildWithChildren(params SyntaxTreeNode[] children) { var builder = new BlockBuilder(); builder.Name = "Test"; builder.Type = BlockType.Comment; foreach (var item in children) { builder.Children.Add(item); } return(builder.Build()); }
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); }
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 TestEmptyVerifier() { byte[] seed = { 0, 0, 0, 0 }; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(seed); Console.WriteLine("preparing the authority block"); KeyPair root = new KeyPair(rng); Biscuit.Token.Builder.BiscuitBuilder builder = Biscuit.Token.Biscuit.Builder(rng, root); builder.AddRight("/folder1/file1", "read"); builder.AddRight("/folder1/file1", "write"); builder.AddRight("/folder1/file2", "read"); builder.AddRight("/folder1/file2", "write"); builder.AddRight("/folder2/file3", "read"); Console.WriteLine(builder.Build()); Biscuit.Token.Biscuit b = builder.Build().Right; Console.WriteLine(b.Print()); BlockBuilder block2 = b.CreateBlock(); block2.ResourcePrefix("/folder1/"); block2.CheckRight("read"); KeyPair keypair2 = new KeyPair(rng); Biscuit.Token.Biscuit b2 = b.Attenuate(rng, keypair2, block2.Build()).Right; Verifier v1 = new Verifier(); v1.Allow(); Either <Error, long> res = v1.Verify(); Assert.IsTrue(res.IsRight); v1.AddToken(b2, Option.Some(root.ToPublicKey())).Get(); v1.AddResource("/folder2/file1"); v1.AddOperation("write"); res = v1.Verify(); Assert.IsTrue(res.IsLeft); }
public Block Block(BlockType type, string name = null, params SyntaxTreeNode[] children) { _last = null; var builder = new BlockBuilder(); builder.Type = type; builder.Name = name; foreach (var child in children) { builder.Children.Add(child); } return(builder.Build()); }
public void ConstructorTransfersInstanceOfChunkGeneratorFromBlockBuilder() { // Arrange var expected = new ExpressionChunkGenerator(); var builder = new BlockBuilder() { Type = BlockType.Helper, ChunkGenerator = expected }; // Act var actual = builder.Build(); // Assert Assert.Same(expected, actual.ChunkGenerator); }
public void ConstructorTransfersChildrenFromBlockBuilder() { // Arrange var expected = new SpanBuilder() { Kind = SpanKind.Code }.Build(); var builder = new BlockBuilder() { Type = BlockType.Functions }; builder.Children.Add(expected); // Act var block = builder.Build(); // Assert Assert.Same(expected, block.Children.Single()); }
public RazorSyntaxTree Execute(RazorCodeDocument document, RazorSyntaxTree syntaxTree) { Block expression; Span route; if (!TryFindPageDirectiveBlocks(syntaxTree.Root, out expression, out route)) { return(syntaxTree); } var builder = new BlockBuilder(syntaxTree.Root); builder.Children.Remove(expression); builder.Children.Remove(route); return(RazorSyntaxTree.Create(builder.Build(), syntaxTree.Diagnostics)); }
protected virtual Block ConfigureAndAddSpanToBlock(BlockBuilder block, SpanConstructor span) { switch (block.Type) { case BlockType.Markup: span.With(new MarkupChunkGenerator()); break; case BlockType.Statement: span.With(new StatementChunkGenerator()); break; case BlockType.Expression: block.ChunkGenerator = new ExpressionChunkGenerator(); span.With(new ExpressionChunkGenerator()); break; } block.Children.Add(span); return(block.Build()); }
/// <summary> /// Ends the current block /// </summary> public void EndBlock() { EnusreNotTerminated(); AssertOnOwnerTask(); if (_blockStack.Count == 0) { throw new InvalidOperationException(RazorResources.EndBlock_Called_Without_Matching_StartBlock); } if (_blockStack.Count > 1) { BlockBuilder block = _blockStack.Pop(); _blockStack.Peek().Children.Add(block.Build()); } else { // If we're at 1, terminate the parser _terminated = true; } }
public void Compile() { Progress(BuildStep.Compiling); Backend.Begin(this); foreach (var upk in Input.Packages) { if (Backend.CanLink(upk)) { upk.Flags |= SourcePackageFlags.CanLink; } } using (Log.StartProfiler(TypeBuilder)) TypeBuilder.Build(); if (Log.HasErrors) { return; } Backend.ShaderBackend.Initialize(this, BundleBuilder); using (Log.StartProfiler(BlockBuilder)) BlockBuilder.Build(); if (Log.HasErrors) { return; } using (Log.StartProfiler(UxlProcessor)) UxlProcessor.CompileDocuments(); if (Log.HasErrors) { return; } Run(new ILAnalyzer(Pass)); Run(ILVerifier); }
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 static Block RebuildChunkGenerators(Block block, bool isBound) { var builder = new BlockBuilder(block); // Don't want to rebuild unbound dynamic attributes. They need to run through the conditional attribute // removal system at runtime. A conditional attribute at the parse tree rewriting level is defined by // having at least 1 child with a DynamicAttributeBlockChunkGenerator. if (!isBound && block.Children.Any( child => child.IsBlock && ((Block)child).ChunkGenerator is DynamicAttributeBlockChunkGenerator)) { // The parent chunk generator must be removed because it's normally responsible for conditionally // generating the attribute prefix (class=") and suffix ("). The prefix and suffix concepts aren't // applicable for the TagHelper use case since the attributes are put into a dictionary like object as // name value pairs. builder.ChunkGenerator = ParentChunkGenerator.Null; return(builder.Build()); } var isDynamic = builder.ChunkGenerator is DynamicAttributeBlockChunkGenerator; // We don't want any attribute specific logic here, null out the block chunk generator. if (isDynamic || builder.ChunkGenerator is AttributeBlockChunkGenerator) { builder.ChunkGenerator = ParentChunkGenerator.Null; } for (var i = 0; i < builder.Children.Count; i++) { var child = builder.Children[i]; if (child.IsBlock) { // The child is a block, recurse down into the block to rebuild its children builder.Children[i] = RebuildChunkGenerators((Block)child, isBound); } else { var childSpan = (Span)child; ISpanChunkGenerator newChunkGenerator = null; var literalGenerator = childSpan.ChunkGenerator as LiteralAttributeChunkGenerator; if (literalGenerator != null) { if (literalGenerator.ValueGenerator == null || literalGenerator.ValueGenerator.Value == null) { newChunkGenerator = new MarkupChunkGenerator(); } else { newChunkGenerator = literalGenerator.ValueGenerator.Value; } } else if (isDynamic && childSpan.ChunkGenerator == SpanChunkGenerator.Null) { // Usually the dynamic chunk generator handles creating the null chunk generators underneath // it. This doesn't make sense in terms of tag helpers though, we need to change null code // generators to markup chunk generators. newChunkGenerator = new MarkupChunkGenerator(); } // If we have a new chunk generator we'll need to re-build the child if (newChunkGenerator != null) { var childSpanBuilder = new SpanBuilder(childSpan) { ChunkGenerator = newChunkGenerator }; builder.Children[i] = childSpanBuilder.Build(); } } } return(builder.Build()); }
private static TryParseResult TryParseBlock( string tagName, Block block, IEnumerable <TagHelperDescriptor> descriptors, ErrorSink errorSink) { // TODO: Accept more than just spans: https://github.com/aspnet/Razor/issues/96. // The first child will only ever NOT be a Span if a user is doing something like: // <input @checked /> var childSpan = block.Children.First() as Span; if (childSpan == null || childSpan.Kind != SpanKind.Markup) { errorSink.OnError( block.Start, RazorResources.FormatTagHelpers_CannotHaveCSharpInTagDeclaration(tagName), block.Length); return(null); } var builder = new BlockBuilder(block); // If there's only 1 child it means that it's plain text inside of the attribute. // i.e. <div class="plain text in attribute"> if (builder.Children.Count == 1) { return(TryParseSpan(childSpan, descriptors, errorSink)); } var nameSymbols = childSpan .Symbols .OfType <HtmlSymbol>() .SkipWhile(symbol => !HtmlMarkupParser.IsValidAttributeNameSymbol(symbol)) // Skip prefix .TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol)) .Select(nameSymbol => nameSymbol.Content); var name = string.Concat(nameSymbols); if (string.IsNullOrEmpty(name)) { errorSink.OnError( childSpan.Start, RazorResources.FormatTagHelpers_AttributesMustHaveAName(tagName), childSpan.Length); return(null); } // Have a name now. Able to determine correct isBoundNonStringAttribute value. var result = CreateTryParseResult(name, descriptors); var firstChild = builder.Children[0] as Span; if (firstChild != null && firstChild.Symbols[0] is HtmlSymbol) { var htmlSymbol = firstChild.Symbols[firstChild.Symbols.Count - 1] as HtmlSymbol; switch (htmlSymbol.Type) { // Treat NoQuotes and DoubleQuotes equivalently. We purposefully do not persist NoQuotes // ValueStyles at code generation time to protect users from rendering dynamic content with spaces // that can break attributes. // Ex: <tag my-attribute=@value /> where @value results in the test "hello world". // This way, the above code would render <tag my-attribute="hello world" />. case HtmlSymbolType.Equals: case HtmlSymbolType.DoubleQuote: result.AttributeValueStyle = HtmlAttributeValueStyle.DoubleQuotes; break; case HtmlSymbolType.SingleQuote: result.AttributeValueStyle = HtmlAttributeValueStyle.SingleQuotes; break; default: result.AttributeValueStyle = HtmlAttributeValueStyle.Minimized; break; } } // Remove first child i.e. foo=" builder.Children.RemoveAt(0); // Grabbing last child to check if the attribute value is quoted. var endNode = block.Children.Last(); if (!endNode.IsBlock) { var endSpan = (Span)endNode; // In some malformed cases e.g. <p bar="false', the last Span (false' in the ex.) may contain more // than a single HTML symbol. Do not ignore those other symbols. var symbolCount = endSpan.Symbols.Count(); var endSymbol = symbolCount == 1 ? (HtmlSymbol)endSpan.Symbols.First() : null; // Checking to see if it's a quoted attribute, if so we should remove end quote if (endSymbol != null && IsQuote(endSymbol)) { builder.Children.RemoveAt(builder.Children.Count - 1); } } // We need to rebuild the chunk generators of the builder and its children (this is needed to // ensure we don't do special attribute chunk generation since this is a tag helper). block = RebuildChunkGenerators(builder.Build(), result.IsBoundAttribute); // If there's only 1 child at this point its value could be a simple markup span (treated differently than // block level elements for attributes). if (block.Children.Count() == 1) { var child = block.Children.First() as Span; if (child != null) { // After pulling apart the block we just have a value span. var spanBuilder = new SpanBuilder(child); result.AttributeValueNode = CreateMarkupAttribute(spanBuilder, result.IsBoundNonStringAttribute); return(result); } } var isFirstSpan = true; result.AttributeValueNode = ConvertToMarkupAttributeBlock( block, (parentBlock, span) => { // If the attribute was requested by a tag helper but the corresponding property was not a // string, then treat its value as code. A non-string value can be any C# value so we need // to ensure the SyntaxTreeNode reflects that. if (result.IsBoundNonStringAttribute) { // For bound non-string attributes, we'll only allow a transition span to appear at the very // beginning of the attribute expression. All later transitions would appear as code so that // they are part of the generated output. E.g. // key="@value" -> MyTagHelper.key = value // key=" @value" -> MyTagHelper.key = @value // key="1 + @case" -> MyTagHelper.key = 1 + @case // key="@int + @case" -> MyTagHelper.key = int + @case // key="@(a + b) -> MyTagHelper.key = a + b // key="4 + @(a + b)" -> MyTagHelper.key = 4 + @(a + b) if (isFirstSpan && span.Kind == SpanKind.Transition) { // do nothing. } else { var spanBuilder = new SpanBuilder(span); if (parentBlock.Type == BlockType.Expression && (spanBuilder.Kind == SpanKind.Transition || spanBuilder.Kind == SpanKind.MetaCode)) { // Change to a MarkupChunkGenerator so that the '@' \ parenthesis is generated as part of the output. spanBuilder.ChunkGenerator = new MarkupChunkGenerator(); } spanBuilder.Kind = SpanKind.Code; span = spanBuilder.Build(); } } isFirstSpan = false; return(span); }); return(result); }
public void Rewrite(RewritingContext context) { RewriteTags(context.SyntaxTree, context); context.SyntaxTree = _currentBlock.Build(); }