GitHub PullRequestReviewCommentEvent at 2014-09-11 04:46:31
Repository: Razor
Actor: dougbu
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/aspnet/Razor/pulls/comments/17404088","id":17404088,"diff_hunk":"@@ -0,0 +1,309 @@\n+﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.\n+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n+\n+using System;\n+using System.Collections.Generic;\n+using System.Linq;\n+using Microsoft.AspNet.Razor.Generator;\n+using Microsoft.AspNet.Razor.Parser.SyntaxTree;\n+using Microsoft.AspNet.Razor.Tokenizer.Symbols;\n+\n+namespace Microsoft.AspNet.Razor.Parser.TagHelpers\n+{\n+    /// <summary>\n+    /// A <see cref=\"BlockBuilder\"/> used to create <see cref=\"TagHelperBlock\"/>s.\n+    /// </summary>\n+    public class TagHelperBlockBuilder : BlockBuilder\n+    {\n+        /// <summary>\n+        /// Instantiates a new <see cref=\"TagHelperBlockBuilder\"/> instance based on given the \n+        /// <paramref name=\"original\"/>.\n+        /// </summary>\n+        /// <param name=\"original\">The original <see cref=\"TagHelperBlock\"/> to copy data from.</param>\n+        public TagHelperBlockBuilder(TagHelperBlock original)\n+            : base(original)\n+        {\n+            TagName = original.TagName;\n+            Attributes = new Dictionary<string, SyntaxTreeNode>(original.Attributes);\n+        }\n+\n+        /// <summary>\n+        /// Instantiates a new instance of the <see cref=\"TagHelperBlockBuilder\"/> class\n+        /// with the provided <paramref name=\"tagName\"/> and derives its <see cref=\"Attributes\"/>\n+        /// and <see cref=\"BlockBuilder.Type\"/> from the <paramref name=\"startTag\"/>.\n+        /// </summary>\n+        /// <param name=\"tagName\">An HTML tag name.</param>\n+        /// <param name=\"startTag\">The <see cref=\"Block\"/> that contains all information about the start\n+        /// of the HTML element.</param>\n+        public TagHelperBlockBuilder(string tagName, Block startTag)\n+        {\n+            TagName = tagName;\n+            CodeGenerator = new TagHelperCodeGenerator();\n+            Type = startTag.Type;\n+            Attributes = GetTagAttributes(startTag);\n+        }\n+\n+        // Internal for testing\n+        internal TagHelperBlockBuilder(string tagName,\n+                                       IDictionary<string, SyntaxTreeNode> attributes,\n+                                       IEnumerable<SyntaxTreeNode> children)\n+        {\n+            TagName = tagName;\n+            Attributes = attributes;\n+            Type = BlockType.Tag;\n+            CodeGenerator = new TagHelperCodeGenerator();\n+\n+            // Children is IList, no AddRange\n+            foreach (var child in children)\n+            {\n+                Children.Add(child);\n+            }\n+        }\n+\n+        /// <summary>\n+        /// The HTML attributes.\n+        /// </summary>\n+        public IDictionary<string, SyntaxTreeNode> Attributes { get; private set; }\n+\n+        /// <summary>\n+        /// The HTML tag name.\n+        /// </summary>\n+        public string TagName { get; set; }\n+\n+        /// <summary>\n+        /// Constructs a new <see cref=\"TagHelperBlock\"/>.\n+        /// </summary>\n+        /// <returns>A <see cref=\"TagHelperBlock\"/>.</returns>\n+        public override Block Build()\n+        {\n+            return new TagHelperBlock(this);\n+        }\n+\n+        /// <inheritdoc />\n+        /// <remarks>\n+        /// Sets the <see cref=\"TagName\"/> to <c>null</c> and clears the <see cref=\"Attributes\"/>.\n+        /// </remarks>\n+        public override void Reset()\n+        {\n+            TagName = null;\n+\n+            if (Attributes != null)\n+            {\n+                Attributes.Clear();\n+            }\n+\n+            base.Reset();\n+        }\n+\n+        private static IDictionary<string, SyntaxTreeNode> GetTagAttributes(Block tagBlock)\n+        {\n+            var attributes = new Dictionary<string, SyntaxTreeNode>(StringComparer.OrdinalIgnoreCase);\n+\n+            // TODO: Handle malformed tags: https://github.com/aspnet/razor/issues/104\n+\n+            // We skip the first child \"<tagname\" and take everything up to the \"ending\" portion of the tag \">\" or \"/>\".\n+            // The -2 accounts for both the start and end tags.\n+            var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - 2);\n+\n+            foreach (var child in attributeChildren)\n+            {\n+                KeyValuePair<string, SyntaxTreeNode> attribute;\n+\n+                if (child.IsBlock)\n+                {\n+                    attribute = ParseBlock((Block)child);\n+                }\n+                else\n+                {\n+                    attribute = ParseSpan((Span)child);\n+                }\n+\n+                attributes.Add(attribute.Key, attribute.Value);\n+            }\n+\n+            return attributes;\n+        }\n+\n+        // This method handles cases when the attribute is a simple span attribute such as\n+        // class=\"something moresomething\".  This does not handle complex attributes such as\n+        // class=\"@myclass\". Therefore the span.Content is equivalent to the entire attribute.\n+        private static KeyValuePair<string, SyntaxTreeNode> ParseSpan(Span span)\n+        {\n+            var afterEquals = false;\n+            var builder = new SpanBuilder\n+            {\n+                CodeGenerator = span.CodeGenerator,\n+                EditHandler = span.EditHandler,\n+                Kind = span.Kind\n+            };\n+            var htmlSymbols = span.Symbols.OfType<HtmlSymbol>().ToArray();\n+            var symbolOffset = 1;\n+            string name = null;\n+\n+            // Iterate down through the symbols to find the name and the start of the value.\n+            // We subtract the symbolOffset so we don't accept an ending quote of a span.\n+            for (var i = 0; i < htmlSymbols.Length - symbolOffset; i++)\n+            {\n+                var symbol = htmlSymbols[i];\n+\n+                if (name == null && symbol.Type == HtmlSymbolType.Text)\n+                {\n+                    name = symbol.Content;\n+                }\n+                else if (symbol.Type == HtmlSymbolType.Equals)\n+                {\n+                    // We've found an '=' symbol, this means that the coming symbols will either be a quote\n+                    // or value (in the case that the value is unquoted).\n+                    // Spaces after/before the equal symbol are not yet supported: \n+                    // https://github.com/aspnet/Razor/issues/123\n+\n+                    // TODO: Handle malformed tags, if there's an '=' then there MUST be a value.\n+                    // https://github.com/aspnet/Razor/issues/104\n+\n+                    // Check for attribute start values, aka single or double quote\n+                    if (IsQuote(htmlSymbols[i + 1]))\n+                    {\n+                        // Move past the attribute start so we can accept the true value.\n+                        i++;\n+                    }\n+                    else\n+                    {\n+                        // Set the symbol offset to 0 so we don't attemp to skip an end quote that doesn't exist.\n+                        symbolOffset = 0;\n+                    }\n+\n+                    afterEquals = true;\n+                }\n+                else if (afterEquals)\n+                {\n+                    builder.Accept(symbol);\n+                }\n+            }\n+\n+            return new KeyValuePair<string, SyntaxTreeNode>(name, builder.Build());\n+        }\n+\n+        private static KeyValuePair<string, SyntaxTreeNode> ParseBlock(Block block)\n+        {\n+            // TODO: Accept more than just spans: https://github.com/aspnet/Razor/issues/96.\n+            // The first child will only ever NOT be a Span if a user is doing something like:\n+            // <input @checked />","path":"src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs","position":190,"original_position":190,"commit_id":"fc8446c478d21afa6bb1e0802abee48fbcaf1c1b","original_commit_id":"fc8446c478d21afa6bb1e0802abee48fbcaf1c1b","user":{"login":"dougbu","id":6431421,"avatar_url":"https://avatars.githubusercontent.com/u/6431421?v=2","gravatar_id":"500f85f8a9c6f038eae5124139fd4d12","url":"https://api.github.com/users/dougbu","html_url":"https://github.com/dougbu","followers_url":"https://api.github.com/users/dougbu/followers","following_url":"https://api.github.com/users/dougbu/following{/other_user}","gists_url":"https://api.github.com/users/dougbu/gists{/gist_id}","starred_url":"https://api.github.com/users/dougbu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dougbu/subscriptions","organizations_url":"https://api.github.com/users/dougbu/orgs","repos_url":"https://api.github.com/users/dougbu/repos","events_url":"https://api.github.com/users/dougbu/events{/privacy}","received_events_url":"https://api.github.com/users/dougbu/received_events","type":"User","site_admin":false},"body":"nevermind","created_at":"2014-09-11T04:46:31Z","updated_at":"2014-09-11T04:46:31Z","html_url":"https://github.com/aspnet/Razor/pull/109#discussion_r17404088","pull_request_url":"https://api.github.com/repos/aspnet/Razor/pulls/109","_links":{"self":{"href":"https://api.github.com/repos/aspnet/Razor/pulls/comments/17404088"},"html":{"href":"https://github.com/aspnet/Razor/pull/109#discussion_r17404088"},"pull_request":{"href":"https://api.github.com/repos/aspnet/Razor/pulls/109"}}},"pull_request":{"url":"https://api.github.com/repos/aspnet/Razor/pulls/109","id":20483529,"html_url":"https://github.com/aspnet/Razor/pull/109","diff_url":"https://github.com/aspnet/Razor/pull/109.diff","patch_url":"https://github.com/aspnet/Razor/pull/109.patch","issue_url":"https://api.github.com/repos/aspnet/Razor/issues/109","number":109,"state":"open","locked":false,"title":"Add TagHelper parse tree rewriter.","user":{"login":"NTaylorMullen","id":2008729,"avatar_url":"https://avatars.githubusercontent.com/u/2008729?v=2","gravatar_id":"4442bee53d2366d1020616c5bfab3ebe","url":"https://api.github.com/users/NTaylorMullen","html_url":"https://github.com/NTaylorMullen","followers_url":"https://api.github.com/users/NTaylorMullen/followers","following_url":"https://api.github.com/users/NTaylorMullen/following{/other_user}","gists_url":"https://api.github.com/users/NTaylorMullen/gists{/gist_id}","starred_url":"https://api.github.com/users/NTaylorMullen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NTaylorMullen/subscriptions","organizations_url":"https://api.github.com/users/NTaylorMullen/orgs","repos_url":"https://api.github.com/users/NTaylorMullen/repos","events_url":"https://api.github.com/users/NTaylorMullen/events{/privacy}","received_events_url":"https://api.github.com/users/NTaylorMullen/received_events","type":"User","site_admin":false},"body":"- The visitor looks for TagBlock's that match registered TagHelpers and rebuilds them into TagHelperBlock's.\r\n- Added the code generator and corresponding chunk for a TagHelperBlock.\r\n- Added syntax tree specific objects & helper methods to create accurate tag helper structures.\r\n\r\n#71 \r\n\r\n**NOTE:** This does not support malformed tag helpers.  It expects all tags (that are tag helpers) are validly formed.  That fix will come with: https://github.com/aspnet/Razor/issues/104\r\n\r\n*Also* I opened a separate test PR due to size here: #110\r\n\r\nIssue: #71","created_at":"2014-08-29T06:26:37Z","updated_at":"2014-09-11T04:46:31Z","closed_at":null,"merged_at":null,"merge_commit_sha":"00ba917cb599a5d3f082e2e350b6937a3f78940b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/aspnet/Razor/pulls/109/commits","review_comments_url":"https://api.github.com/repos/aspnet/Razor/pulls/109/comments","review_comment_url":"https://api.github.com/repos/aspnet/Razor/pulls/comments/{number}","comments_url":"https://api.github.com/repos/aspnet/Razor/issues/109/comments","statuses_url":"https://api.github.com/repos/aspnet/Razor/statuses/fc8446c478d21afa6bb1e0802abee48fbcaf1c1b","head":{"label":"aspnet:TagHelpers_ParseTreeRewriter","ref":"TagHelpers_ParseTreeRewriter","sha":"fc8446c478d21afa6bb1e0802abee48fbcaf1c1b","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=2","gravatar_id":null,"url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":15835991,"name":"Razor","full_name":"aspnet/Razor","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=2","gravatar_id":null,"url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/Razor","description":"","fork":false,"url":"https://api.github.com/repos/aspnet/Razor","forks_url":"https://api.github.com/repos/aspnet/Razor/forks","keys_url":"https://api.github.com/repos/aspnet/Razor/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/Razor/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/Razor/teams","hooks_url":"https://api.github.com/repos/aspnet/Razor/hooks","issue_events_url":"https://api.github.com/repos/aspnet/Razor/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/Razor/events","assignees_url":"https://api.github.com/repos/aspnet/Razor/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/Razor/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/Razor/tags","blobs_url":"https://api.github.com/repos/aspnet/Razor/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/Razor/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/Razor/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/Razor/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/Razor/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/Razor/languages","stargazers_url":"https://api.github.com/repos/aspnet/Razor/stargazers","contributors_url":"https://api.github.com/repos/aspnet/Razor/contributors","subscribers_url":"https://api.github.com/repos/aspnet/Razor/subscribers","subscription_url":"https://api.github.com/repos/aspnet/Razor/subscription","commits_url":"https://api.github.com/repos/aspnet/Razor/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/Razor/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/Razor/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/Razor/issues/comments/{number}","contents_url":"https://api.github.com/repos/aspnet/Razor/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/Razor/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/Razor/merges","archive_url":"https://api.github.com/repos/aspnet/Razor/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/Razor/downloads","issues_url":"https://api.github.com/repos/aspnet/Razor/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/Razor/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/Razor/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/Razor/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/Razor/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/Razor/releases{/id}","created_at":"2014-01-12T03:52:46Z","updated_at":"2014-09-08T01:13:41Z","pushed_at":"2014-09-10T23:47:26Z","git_url":"git://github.com/aspnet/Razor.git","ssh_url":"git@github.com:aspnet/Razor.git","clone_url":"https://github.com/aspnet/Razor.git","svn_url":"https://github.com/aspnet/Razor","homepage":null,"size":4148,"stargazers_count":169,"watchers_count":169,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":33,"mirror_url":null,"open_issues_count":64,"forks":33,"open_issues":64,"watchers":169,"default_branch":"dev"}},"base":{"label":"aspnet:TagHelpers_RegistrationSystem","ref":"TagHelpers_RegistrationSystem","sha":"489caed17c718faf7e39f7fa5f0f188f69afcb62","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=2","gravatar_id":null,"url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":15835991,"name":"Razor","full_name":"aspnet/Razor","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=2","gravatar_id":null,"url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/Razor","description":"","fork":false,"url":"https://api.github.com/repos/aspnet/Razor","forks_url":"https://api.github.com/repos/aspnet/Razor/forks","keys_url":"https://api.github.com/repos/aspnet/Razor/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/Razor/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/Razor/teams","hooks_url":"https://api.github.com/repos/aspnet/Razor/hooks","issue_events_url":"https://api.github.com/repos/aspnet/Razor/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/Razor/events","assignees_url":"https://api.github.com/repos/aspnet/Razor/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/Razor/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/Razor/tags","blobs_url":"https://api.github.com/repos/aspnet/Razor/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/Razor/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/Razor/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/Razor/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/Razor/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/Razor/languages","stargazers_url":"https://api.github.com/repos/aspnet/Razor/stargazers","contributors_url":"https://api.github.com/repos/aspnet/Razor/contributors","subscribers_url":"https://api.github.com/repos/aspnet/Razor/subscribers","subscription_url":"https://api.github.com/repos/aspnet/Razor/subscription","commits_url":"https://api.github.com/repos/aspnet/Razor/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/Razor/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/Razor/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/Razor/issues/comments/{number}","contents_url":"https://api.github.com/repos/aspnet/Razor/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/Razor/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/Razor/merges","archive_url":"https://api.github.com/repos/aspnet/Razor/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/Razor/downloads","issues_url":"https://api.github.com/repos/aspnet/Razor/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/Razor/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/Razor/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/Razor/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/Razor/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/Razor/releases{/id}","created_at":"2014-01-12T03:52:46Z","updated_at":"2014-09-08T01:13:41Z","pushed_at":"2014-09-10T23:47:26Z","git_url":"git://github.com/aspnet/Razor.git","ssh_url":"git@github.com:aspnet/Razor.git","clone_url":"https://github.com/aspnet/Razor.git","svn_url":"https://github.com/aspnet/Razor","homepage":null,"size":4148,"stargazers_count":169,"watchers_count":169,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":33,"mirror_url":null,"open_issues_count":64,"forks":33,"open_issues":64,"watchers":169,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/aspnet/Razor/pulls/109"},"html":{"href":"https://github.com/aspnet/Razor/pull/109"},"issue":{"href":"https://api.github.com/repos/aspnet/Razor/issues/109"},"comments":{"href":"https://api.github.com/repos/aspnet/Razor/issues/109/comments"},"review_comments":{"href":"https://api.github.com/repos/aspnet/Razor/pulls/109/comments"},"review_comment":{"href":"https://api.github.com/repos/aspnet/Razor/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/aspnet/Razor/pulls/109/commits"},"statuses":{"href":"https://api.github.com/repos/aspnet/Razor/statuses/fc8446c478d21afa6bb1e0802abee48fbcaf1c1b"}}}}