public void GetCompletionAt_AtAttributeEdge_IndexerAttribute_ReturnsCompletionsWithoutSnippet() { // Arrange var tagHelper = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly"); tagHelper.TagMatchingRule(rule => rule.TagName = "test"); tagHelper.SetTypeName("TestTagHelper"); tagHelper.BindAttribute(attribute => { attribute.Name = "int-val"; attribute.SetPropertyName("IntVal"); attribute.TypeName = ("System.Collections.Generic.IDictionary<System.String, System.Int32>"); attribute.AsDictionary("int-val-", typeof(int).FullName); }); var service = new DefaultTagHelperCompletionService(RazorTagHelperCompletionService, HtmlFactsService, TagHelperFactsService); var codeDocument = CreateCodeDocument($"@addTagHelper *, TestAssembly{Environment.NewLine}<test />", tagHelper.Build()); var sourceSpan = new SourceSpan(35 + Environment.NewLine.Length, 0); // Act var completions = service.GetCompletionsAt(sourceSpan, codeDocument); // Assert Assert.Collection( completions, completion => { Assert.Equal("int-val", completion.InsertText); Assert.Equal(InsertTextFormat.PlainText, completion.InsertTextFormat); Assert.Equal(new[] { "=" }, completion.CommitCharacters); }, completion => { Assert.Equal("int-val-", completion.InsertText); Assert.Equal(InsertTextFormat.PlainText, completion.InsertTextFormat); Assert.Equal(Array.Empty <string>(), completion.CommitCharacters); }); }
public async Task Handle_ProvidesInjectOnIncomplete() { // Arrange var documentPath = "C:/path/to/document.razor"; var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "TestTagHelper", "TestAssembly"); builder.TagMatchingRule(rule => rule.TagName = "Test"); builder.SetTypeName("TestNamespace.TestTagHelper"); var tagHelper = builder.Build(); var tagHelperContext = TagHelperDocumentContext.Create(prefix: string.Empty, new[] { tagHelper }); var codeDocument = CreateCodeDocument("@inje"); codeDocument.SetTagHelperContext(tagHelperContext); var documentResolver = CreateDocumentResolver(documentPath, codeDocument); var completionEndpoint = new RazorCompletionEndpoint( Dispatcher, documentResolver, CompletionFactsService, CompletionListCache, LoggerFactory); completionEndpoint.GetRegistration(ClientCapabilities); var request = new VSCompletionParamsBridge() { TextDocument = new TextDocumentIdentifier() { Uri = new Uri(documentPath) }, Position = new Position(0, 1), Context = new VSInternalCompletionContext() { TriggerKind = CompletionTriggerKind.TriggerForIncompleteCompletions, }, }; // Act var completionList = await Task.Run(() => completionEndpoint.Handle(request, default)); // Assert Assert.Contains(completionList.Items, item => item.InsertText == "addTagHelper"); }
public void GetElementCompletions_TagHelperPrefixIsPrependedToTagHelperCompletions() { // Arrange var documentDescriptors = new[] { TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli")) .Build(), TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("li")) .Build(), }; var expectedCompletions = ElementCompletionResult.Create(new Dictionary <string, HashSet <TagHelperDescriptor> >() { ["th:superli"] = new HashSet <TagHelperDescriptor> { documentDescriptors[0] }, ["th:li"] = new HashSet <TagHelperDescriptor> { documentDescriptors[1] }, ["li"] = new HashSet <TagHelperDescriptor>(), }); var existingCompletions = new[] { "li" }; var completionContext = BuildElementCompletionContext( documentDescriptors, existingCompletions, containingTagName: "ul", tagHelperPrefix: "th:"); var service = CreateTagHelperCompletionFactsService(); // Act var completions = service.GetElementCompletions(completionContext); // Assert AssertCompletionsAreEquivalent(expectedCompletions, completions); }
public void TagHelperAttributesAreLocatedAndAcceptChangesCorrectly(object editObject, object partialParseResultObject) { // Arrange var edit = (TestEdit)editObject; var partialParseResult = (PartialParseResultInternal)partialParseResultObject; var builder = TagHelperDescriptorBuilder.Create("PTagHelper", "Test"); builder.SetTypeName("PTagHelper"); builder.TagMatchingRule(rule => rule.TagName = "p"); builder.BindAttribute(attribute => { attribute.Name = "obj-attr"; attribute.TypeName = typeof(object).FullName; attribute.SetPropertyName("ObjectAttribute"); }); builder.BindAttribute(attribute => { attribute.Name = "str-attr"; attribute.TypeName = typeof(string).FullName; attribute.SetPropertyName("StringAttribute"); }); var descriptors = new[] { builder.Build() }; var projectEngine = CreateProjectEngine(tagHelpers: descriptors); var sourceDocument = new TestRazorProjectItem("Index.cshtml") { Content = edit.OldSnapshot.GetText() }; var codeDocument = projectEngine.Process(sourceDocument); var syntaxTree = codeDocument.GetSyntaxTree(); var parser = new RazorSyntaxTreePartialParser(syntaxTree); // Act var(result, _) = parser.Parse(edit.Change); // Assert Assert.Equal(partialParseResult, result); }
public DefaultProjectSnapshotManagerProxyTest() { Workspace = TestWorkspace.Create(); var projectWorkspaceState1 = new ProjectWorkspaceState(new[] { TagHelperDescriptorBuilder.Create("test1", "TestAssembly1").Build(), }, default); ProjectSnapshot1 = new DefaultProjectSnapshot( ProjectState.Create( Workspace.Services, new HostProject("/host/path/to/project1.csproj", RazorConfiguration.Default, "project1"), projectWorkspaceState1)); var projectWorkspaceState2 = new ProjectWorkspaceState(new[] { TagHelperDescriptorBuilder.Create("test2", "TestAssembly2").Build(), }, default); ProjectSnapshot2 = new DefaultProjectSnapshot( ProjectState.Create( Workspace.Services, new HostProject("/host/path/to/project2.csproj", RazorConfiguration.Default, "project2"), projectWorkspaceState2)); }
public void GetElementCompletions_NewCompletionsForSchemaTagsNotInExistingCompletionsAreIgnored() { // Arrange var documentDescriptors = new[] { TagHelperDescriptorBuilder.Create("SuperLiTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("superli")) .Build(), TagHelperDescriptorBuilder.Create("LiTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("li")) .TagOutputHint("strong") .Build(), TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("div")) .Build(), }; var expectedCompletions = ElementCompletionResult.Create(new Dictionary <string, HashSet <TagHelperDescriptor> >() { ["li"] = new HashSet <TagHelperDescriptor> { documentDescriptors[1] }, ["superli"] = new HashSet <TagHelperDescriptor> { documentDescriptors[0] }, }); var existingCompletions = new[] { "li" }; var completionContext = BuildElementCompletionContext(documentDescriptors, existingCompletions, containingTagName: "ul"); var service = CreateTagHelperCompletionFactsService(); // Act var completions = service.GetElementCompletions(completionContext); // Assert AssertCompletionsAreEquivalent(expectedCompletions, completions); }
public void GetElementCompletions_TagOutputHintDoesNotFallThroughToSchemaCheck() { // Arrange var documentDescriptors = new[] { TagHelperDescriptorBuilder.Create("MyTableTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("my-table")) .TagOutputHint("table") .Build(), TagHelperDescriptorBuilder.Create("MyTrTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("my-tr")) .TagOutputHint("tr") .Build(), }; var expectedCompletions = ElementCompletionResult.Create(new Dictionary <string, HashSet <TagHelperDescriptor> >() { ["my-table"] = new HashSet <TagHelperDescriptor> { documentDescriptors[0] }, ["table"] = new HashSet <TagHelperDescriptor>(), }); var existingCompletions = new[] { "table" }; var completionContext = BuildElementCompletionContext( documentDescriptors, existingCompletions, containingTagName: "body", containingParentTagName: null); var service = CreateTagHelperCompletionFactsService(); // Act var completions = service.GetElementCompletions(completionContext); // Assert AssertCompletionsAreEquivalent(expectedCompletions, completions); }
public void GetAttributeCompletions_PossibleDescriptorsReturnUnboundRequiredAttributesWithExistingCompletions() { // Arrange var documentDescriptors = new[] { TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule .RequireTagName("div") .RequireAttributeDescriptor(attribute => attribute.Name("repeat"))) .Build(), TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly") .TagMatchingRuleDescriptor(rule => rule .RequireTagName("*") .RequireAttributeDescriptor(attribute => attribute.Name("class"))) .Build(), }; var expectedCompletions = AttributeCompletionResult.Create(new Dictionary <string, HashSet <BoundAttributeDescriptor> >() { ["class"] = new HashSet <BoundAttributeDescriptor>(), ["onclick"] = new HashSet <BoundAttributeDescriptor>(), ["repeat"] = new HashSet <BoundAttributeDescriptor>() }); var existingCompletions = new[] { "onclick", "class" }; var completionContext = BuildAttributeCompletionContext( documentDescriptors, existingCompletions, currentTagName: "div"); var service = CreateTagHelperCompletionFactsService(); // Act var completions = service.GetAttributeCompletions(completionContext); // Assert AssertCompletionsAreEquivalent(expectedCompletions, completions); }
public ProjectStateGeneratedOutputTest() { HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0); HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0); var projectId = ProjectId.CreateNewId("Test"); var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create( projectId, VersionStamp.Default, "Test", "Test", LanguageNames.CSharp, TestProjectData.SomeProject.FilePath)); WorkspaceProject = solution.GetProject(projectId); SomeTagHelpers = new List <TagHelperDescriptor>(); SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build()); HostDocument = TestProjectData.SomeProjectFile1; Text = SourceText.From("Hello, world!"); TextLoader = () => Task.FromResult(TextAndVersion.Create(Text, VersionStamp.Create())); }
public void GetBinding_CaseSensitiveRule_CaseMismatch_ReturnsNull() { // Arrange var divTagHelper = TagHelperDescriptorBuilder.Create("DivTagHelper", "SomeAssembly") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("div")) .SetCaseSensitive() .Build(); var expectedDescriptors = new[] { divTagHelper }; var expectedAttributes = new[] { new KeyValuePair <string, string>("class", "something") }; var tagHelperBinder = new TagHelperBinder("th:", expectedDescriptors); // Act var bindingResult = tagHelperBinder.GetBinding( tagName: "th:Div", attributes: expectedAttributes, parentTagName: "body", parentIsTagHelper: false); // Assert Assert.Null(bindingResult); }
private TagHelperDescriptor CreateRefTagHelper() { var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Ref.TagHelperKind, "Ref", ComponentsApi.AssemblyName); builder.Documentation = ComponentResources.RefTagHelper_Documentation; builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Ref.TagHelperKind); builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString); builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Ref.RuntimeName; // WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the tooltips. builder.SetTypeName("Microsoft.AspNetCore.Components.Ref"); builder.TagMatchingRule(rule => { rule.TagName = "*"; rule.Attribute(attribute => { attribute.Name = "ref"; }); }); builder.BindAttribute(attribute => { attribute.Documentation = ComponentResources.RefTagHelper_Documentation; attribute.Name = "ref"; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the tooltips. attribute.SetPropertyName("Ref"); attribute.TypeName = typeof(object).FullName; }); return(builder.Build()); }
public ProjectStateTest() { HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0, TestProjectData.SomeProject.RootNamespace); HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0, TestProjectData.SomeProject.RootNamespace); ProjectWorkspaceState = new ProjectWorkspaceState(new[] { TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(), }); SomeTagHelpers = new List <TagHelperDescriptor>(); SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build()); Documents = new HostDocument[] { TestProjectData.SomeProjectFile1, TestProjectData.SomeProjectFile2, // linked file TestProjectData.AnotherProjectNestedFile3, }; Text = SourceText.From("Hello, world!"); TextLoader = () => Task.FromResult(TextAndVersion.Create(Text, VersionStamp.Create())); }
public void CreateDescriptor_ForAsyncViewComponentWithInvokeInBaseType_Works() { // Arrange var testCompilation = TestCompilation.Create(_assembly); var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation); var expectedDescriptor = TagHelperDescriptorBuilder.Create( ViewComponentTagHelperConventions.Kind, "__Generated__AsyncDerivedViewComponentTagHelper", typeof(AsyncDerivedViewComponent).Assembly.GetName().Name) .TypeName("__Generated__AsyncDerivedViewComponentTagHelper") .DisplayName("AsyncDerivedViewComponentTagHelper") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("vc:async-derived")) .AddMetadata(ViewComponentTagHelperMetadata.Name, "AsyncDerived") .Build(); var viewComponent = testCompilation.GetTypeByMetadataName(typeof(AsyncDerivedViewComponent).FullName); // Act var descriptor = factory.CreateDescriptor(viewComponent); // Assert Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.CaseSensitive); }
private static TagHelperDescriptor CreateTagHelperDescriptor( string tagName, string typeName, string assemblyName, IEnumerable <Action <BoundAttributeDescriptorBuilder> > attributes = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); builder.TypeName(typeName); if (attributes != null) { foreach (var attributeBuilder in attributes) { builder.BindAttribute(attributeBuilder); } } builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName)); var descriptor = builder.Build(); return(descriptor); }
public DefaultVisualStudioDocumentTrackerTest() { RazorCoreContentType = Mock.Of <IContentType>(c => c.IsOfType(RazorLanguage.ContentType) == true); TextBuffer = Mock.Of <ITextBuffer>(b => b.ContentType == RazorCoreContentType); FilePath = TestProjectData.SomeProjectFile1.FilePath; ProjectPath = TestProjectData.SomeProject.FilePath; RootNamespace = TestProjectData.SomeProject.RootNamespace; ImportDocumentManager = Mock.Of <ImportDocumentManager>(); WorkspaceEditorSettings = new DefaultWorkspaceEditorSettings(Mock.Of <ForegroundDispatcher>(), Mock.Of <EditorSettingsManager>()); SomeTagHelpers = new List <TagHelperDescriptor>() { TagHelperDescriptorBuilder.Create("test", "test").Build(), }; ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace) { AllowNotifyListeners = true }; HostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_1, RootNamespace); UpdatedHostProject = new HostProject(ProjectPath, FallbackRazorConfiguration.MVC_2_0, RootNamespace); OtherHostProject = new HostProject(TestProjectData.AnotherProject.FilePath, FallbackRazorConfiguration.MVC_2_0, TestProjectData.AnotherProject.RootNamespace); DocumentTracker = new DefaultVisualStudioDocumentTracker( Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); }
public void HaveTagHelpersChanged_TagHelpersUpdated_ReturnsTrue() { // Arrange var hostProject = new HostProject("Test1.csproj", RazorConfiguration.Default); var workspaceProject = GetWorkspaceProject("Test1"); var original = new DefaultProjectSnapshot(hostProject, workspaceProject); var anotherProject = GetWorkspaceProject("Test1"); var update = new ProjectSnapshotUpdateContext("Test1.csproj", hostProject, anotherProject, VersionStamp.Default) { TagHelpers = new[] { TagHelperDescriptorBuilder.Create("One", "TestAssembly").Build(), TagHelperDescriptorBuilder.Create("Two", "TestAssembly").Build(), }, }; var snapshot = original.WithComputedUpdate(update); // Act var result = snapshot.HaveTagHelpersChanged(original); // Assert Assert.True(result); }
private List <TagHelperDescriptor> CreateComponentBindTagHelpers(ICollection <TagHelperDescriptor> tagHelpers) { var results = new List <TagHelperDescriptor>(); foreach (var tagHelper in tagHelpers) { if (!tagHelper.IsComponentTagHelper()) { continue; } // We want to create a 'bind' tag helper everywhere we see a pair of properties like `Foo`, `FooChanged` // where `FooChanged` is a delegate and `Foo` is not. // // The easiest way to figure this out without a lot of backtracking is to look for `FooChanged` and then // try to find a matching "Foo". // // We also look for a corresponding FooExpression attribute, though its presence is optional. for (var i = 0; i < tagHelper.BoundAttributes.Count; i++) { var changeAttribute = tagHelper.BoundAttributes[i]; if (!changeAttribute.Name.EndsWith("Changed", StringComparison.Ordinal) || // Allow the ValueChanged attribute to be a delegate or EventCallback<>. // // We assume that the Delegate or EventCallback<> has a matching type, and the C# compiler will help // you figure figure it out if you did it wrongly. (!changeAttribute.IsDelegateProperty() && !changeAttribute.IsEventCallbackProperty())) { continue; } BoundAttributeDescriptor valueAttribute = null; BoundAttributeDescriptor expressionAttribute = null; var valueAttributeName = changeAttribute.Name.Substring(0, changeAttribute.Name.Length - "Changed".Length); var expressionAttributeName = valueAttributeName + "Expression"; for (var j = 0; j < tagHelper.BoundAttributes.Count; j++) { if (tagHelper.BoundAttributes[j].Name == valueAttributeName) { valueAttribute = tagHelper.BoundAttributes[j]; } if (tagHelper.BoundAttributes[j].Name == expressionAttributeName) { expressionAttribute = tagHelper.BoundAttributes[j]; } if (valueAttribute != null && expressionAttribute != null) { // We found both, so we can stop looking now break; } } if (valueAttribute == null) { // No matching attribute found. continue; } var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Bind.TagHelperKind, tagHelper.Name, tagHelper.AssemblyName); builder.DisplayName = tagHelper.DisplayName; builder.CaseSensitive = true; builder.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Bind.TagHelperKind); builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Bind.RuntimeName; builder.Metadata[ComponentMetadata.Bind.ValueAttribute] = valueAttribute.Name; builder.Metadata[ComponentMetadata.Bind.ChangeAttribute] = changeAttribute.Name; if (expressionAttribute != null) { builder.Metadata[ComponentMetadata.Bind.ExpressionAttribute] = expressionAttribute.Name; } // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName(tagHelper.GetTypeName()); // Match the component and attribute name builder.TagMatchingRule(rule => { rule.TagName = tagHelper.TagMatchingRules.Single().TagName; rule.Attribute(attribute => { attribute.Name = "@bind-" + valueAttribute.Name; attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); builder.BindAttribute(attribute => { attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; attribute.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); attribute.Name = "@bind-" + valueAttribute.Name; attribute.TypeName = changeAttribute.TypeName; attribute.IsEnum = valueAttribute.IsEnum; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName(valueAttribute.GetPropertyName()); }); if (tagHelper.IsComponentFullyQualifiedNameMatch()) { builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch; } results.Add(builder.Build()); } } return(results); }
private List <TagHelperDescriptor> CreateElementBindTagHelpers(List <ElementBindData> data) { var results = new List <TagHelperDescriptor>(); for (var i = 0; i < data.Count; i++) { var entry = data[i]; var name = entry.Suffix == null ? "Bind" : "Bind_" + entry.Suffix; var attributeName = entry.Suffix == null ? "@bind" : "@bind-" + entry.Suffix; var formatName = entry.Suffix == null ? "Format_" + entry.ValueAttribute : "Format_" + entry.Suffix; var formatAttributeName = entry.Suffix == null ? "format-" + entry.ValueAttribute : "format-" + entry.Suffix; var eventName = entry.Suffix == null ? "Event_" + entry.ValueAttribute : "Event_" + entry.Suffix; var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Bind.TagHelperKind, name, ComponentsApi.AssemblyName); builder.CaseSensitive = true; builder.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Element_Documentation, entry.ValueAttribute, entry.ChangeAttribute); builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Bind.TagHelperKind); builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString); builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Bind.RuntimeName; builder.Metadata[ComponentMetadata.Bind.ValueAttribute] = entry.ValueAttribute; builder.Metadata[ComponentMetadata.Bind.ChangeAttribute] = entry.ChangeAttribute; builder.Metadata[ComponentMetadata.Bind.IsInvariantCulture] = entry.IsInvariantCulture ? bool.TrueString : bool.FalseString; builder.Metadata[ComponentMetadata.Bind.Format] = entry.Format; if (entry.TypeAttribute != null) { // For entries that map to the <input /> element, we need to be able to know // the difference between <input /> and <input type="text" .../> for which we // want to use the same attributes. // // We provide a tag helper for <input /> that should match all input elements, // but we only want it to be used when a more specific one is used. // // Therefore we use this metadata to know which one is more specific when two // tag helpers match. builder.Metadata[ComponentMetadata.Bind.TypeAttribute] = entry.TypeAttribute; } // WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName(entry.TypeName); builder.TagMatchingRule(rule => { rule.TagName = entry.Element; if (entry.TypeAttribute != null) { rule.Attribute(a => { a.Name = "type"; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; a.Value = entry.TypeAttribute; a.ValueComparisonMode = RequiredAttributeDescriptor.ValueComparisonMode.FullMatch; }); } rule.Attribute(a => { a.Name = attributeName; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; a.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); builder.BindAttribute(a => { a.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; a.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Element_Documentation, entry.ValueAttribute, entry.ChangeAttribute); a.Name = attributeName; a.TypeName = typeof(object).FullName; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. a.SetPropertyName(name); a.BindAttributeParameter(parameter => { parameter.Name = "format"; parameter.TypeName = typeof(string).FullName; parameter.Documentation = string.Format(CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Element_Format_Documentation, attributeName); parameter.SetPropertyName(formatName); }); a.BindAttributeParameter(parameter => { parameter.Name = "event"; parameter.TypeName = typeof(string).FullName; parameter.Documentation = string.Format(CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Element_Event_Documentation, attributeName); parameter.SetPropertyName(eventName); }); a.BindAttributeParameter(parameter => { parameter.Name = "culture"; parameter.TypeName = typeof(CultureInfo).FullName; parameter.Documentation = ComponentResources.BindTagHelper_Element_Culture_Documentation; parameter.SetPropertyName("Culture"); }); }); // This is no longer supported. This is just here so we can add a diagnostic later on when this matches. builder.BindAttribute(attribute => { attribute.Name = formatAttributeName; attribute.TypeName = "System.String"; attribute.Documentation = string.Format(CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Element_Format_Documentation, attributeName); // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName(formatName); }); results.Add(builder.Build()); } return(results); }
private TagHelperDescriptor CreateFallbackBindTagHelper() { var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Bind.TagHelperKind, "Bind", ComponentsApi.AssemblyName); builder.CaseSensitive = true; builder.Documentation = ComponentResources.BindTagHelper_Fallback_Documentation; builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Bind.TagHelperKind); builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString); builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Bind.RuntimeName; builder.Metadata[ComponentMetadata.Bind.FallbackKey] = bool.TrueString; // WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName("Microsoft.AspNetCore.Components.Bind"); builder.TagMatchingRule(rule => { rule.TagName = "*"; rule.Attribute(attribute => { attribute.Name = "@bind-"; attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch; attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); builder.BindAttribute(attribute => { attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; attribute.Documentation = ComponentResources.BindTagHelper_Fallback_Documentation; var attributeName = "@bind-..."; attribute.Name = attributeName; attribute.AsDictionary("@bind-", typeof(object).FullName); // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName("Bind"); attribute.TypeName = "System.Collections.Generic.Dictionary<string, object>"; attribute.BindAttributeParameter(parameter => { parameter.Name = "format"; parameter.TypeName = typeof(string).FullName; parameter.Documentation = ComponentResources.BindTagHelper_Fallback_Format_Documentation; parameter.SetPropertyName("Format"); }); attribute.BindAttributeParameter(parameter => { parameter.Name = "event"; parameter.TypeName = typeof(string).FullName; parameter.Documentation = string.Format(CultureInfo.CurrentCulture, ComponentResources.BindTagHelper_Fallback_Event_Documentation, attributeName); parameter.SetPropertyName("Event"); }); attribute.BindAttributeParameter(parameter => { parameter.Name = "culture"; parameter.TypeName = typeof(CultureInfo).FullName; parameter.Documentation = ComponentResources.BindTagHelper_Element_Culture_Documentation; parameter.SetPropertyName("Culture"); }); }); return(builder.Build()); }
public void DefaultTagHelperOptimizationPass_Execute_ReplacesChildren() { // Arrange var codeDocument = CreateDocument(@" @addTagHelper TestTagHelper, TestAssembly <p foo=""17"" attr=""value"">"); var tagHelpers = new[] { TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly") .TypeName("TestTagHelper") .BoundAttributeDescriptor(attribute => attribute .Name("Foo") .TypeName("System.Int32") .PropertyName("FooProp")) .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p")) .Build() }; var engine = CreateEngine(tagHelpers); var pass = new DefaultTagHelperOptimizationPass() { Engine = engine }; var irDocument = CreateIRDocument(engine, codeDocument); // Act pass.Execute(codeDocument, irDocument); // Assert var @class = irDocument.FindPrimaryClass(); Assert.IsType <DefaultTagHelperRuntimeIntermediateNode>(@class.Children[0]); var fieldDeclaration = Assert.IsType <FieldDeclarationIntermediateNode>(@class.Children[1]); Assert.Equal(bool.TrueString, fieldDeclaration.Annotations[CommonAnnotations.DefaultTagHelperExtension.TagHelperField]); Assert.Equal("__TestTagHelper", fieldDeclaration.FieldName); Assert.Equal("global::TestTagHelper", fieldDeclaration.FieldType); Assert.Equal("private", fieldDeclaration.Modifiers.First()); var tagHelper = FindTagHelperNode(irDocument); Assert.Equal(5, tagHelper.Children.Count); var body = Assert.IsType <DefaultTagHelperBodyIntermediateNode>(tagHelper.Children[0]); Assert.Equal("p", body.TagName); Assert.Equal(TagMode.StartTagAndEndTag, body.TagMode); var create = Assert.IsType <DefaultTagHelperCreateIntermediateNode>(tagHelper.Children[1]); Assert.Equal("__TestTagHelper", create.FieldName); Assert.Equal("TestTagHelper", create.TypeName); Assert.Equal(tagHelpers[0], create.TagHelper, TagHelperDescriptorComparer.Default); var property = Assert.IsType <DefaultTagHelperPropertyIntermediateNode>(tagHelper.Children[2]); Assert.Equal("foo", property.AttributeName); Assert.Equal(AttributeStructure.DoubleQuotes, property.AttributeStructure); Assert.Equal(tagHelpers[0].BoundAttributes[0], property.BoundAttribute, BoundAttributeDescriptorComparer.Default); Assert.Equal("__TestTagHelper", property.FieldName); Assert.False(property.IsIndexerNameMatch); Assert.Equal("FooProp", property.PropertyName); Assert.Equal(tagHelpers[0], property.TagHelper, TagHelperDescriptorComparer.Default); var htmlAttribute = Assert.IsType <DefaultTagHelperHtmlAttributeIntermediateNode>(tagHelper.Children[3]); Assert.Equal("attr", htmlAttribute.AttributeName); Assert.Equal(AttributeStructure.DoubleQuotes, htmlAttribute.AttributeStructure); Assert.IsType <DefaultTagHelperExecuteIntermediateNode>(tagHelper.Children[4]); }
private TagHelperDescriptor CreateDescriptor(INamedTypeSymbol type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } var typeName = type.ToDisplayString(FullNameTypeDisplayFormat); var assemblyName = type.ContainingAssembly.Identity.Name; var builder = TagHelperDescriptorBuilder.Create(ComponentTagHelperKind, typeName, assemblyName); builder.SetTypeName(typeName); // This opts out this 'component' tag helper for any processing that's specific to the default // Razor ITagHelper runtime. builder.Metadata[TagHelperMetadata.Runtime.Name] = "Blazor.IComponent"; var xml = type.GetDocumentationCommentXml(); if (!string.IsNullOrEmpty(xml)) { builder.Documentation = xml; } // Components have very simple matching rules. The type name (short) matches the tag name. builder.TagMatchingRule(r => r.TagName = type.Name); foreach (var property in GetVisibleProperties(type)) { if (property.kind == PropertyKind.Ignored) { continue; } builder.BindAttribute(pb => { pb.Name = property.property.Name; pb.TypeName = property.property.Type.ToDisplayString(FullNameTypeDisplayFormat); pb.SetPropertyName(property.property.Name); if (property.kind == PropertyKind.Enum) { pb.IsEnum = true; } if (property.kind == PropertyKind.Delegate) { pb.Metadata.Add(DelegateSignatureMetadata, bool.TrueString); } xml = property.property.GetDocumentationCommentXml(); if (!string.IsNullOrEmpty(xml)) { pb.Documentation = xml; } }); } var descriptor = builder.Build(); return(descriptor); }
private List <TagHelperDescriptor> CreateComponentBindTagHelpers(ICollection <TagHelperDescriptor> tagHelpers) { var results = new List <TagHelperDescriptor>(); foreach (var tagHelper in tagHelpers) { if (!tagHelper.IsComponentTagHelper()) { continue; } // We want to create a 'bind' tag helper everywhere we see a pair of properties like `Foo`, `FooChanged` // where `FooChanged` is a delegate and `Foo` is not. // // The easiest way to figure this out without a lot of backtracking is to look for `FooChanged` and then // try to find a matching "Foo". for (var i = 0; i < tagHelper.BoundAttributes.Count; i++) { var changeAttribute = tagHelper.BoundAttributes[i]; if (!changeAttribute.Name.EndsWith("Changed") || !changeAttribute.IsDelegateProperty()) { continue; } BoundAttributeDescriptor valueAttribute = null; var valueAttributeName = changeAttribute.Name.Substring(0, changeAttribute.Name.Length - "Changed".Length); for (var j = 0; j < tagHelper.BoundAttributes.Count; j++) { if (tagHelper.BoundAttributes[j].Name == valueAttributeName && !tagHelper.BoundAttributes[j].IsDelegateProperty()) { valueAttribute = tagHelper.BoundAttributes[j]; break; } } if (valueAttribute == null) { // No matching attribute found. continue; } var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.Bind.TagHelperKind, tagHelper.Name, tagHelper.AssemblyName); builder.DisplayName = tagHelper.DisplayName; builder.Documentation = string.Format( Resources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind); builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName; builder.Metadata[BlazorMetadata.Bind.ValueAttribute] = valueAttribute.Name; builder.Metadata[BlazorMetadata.Bind.ChangeAttribute] = changeAttribute.Name; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName(tagHelper.GetTypeName()); // Match the component and attribute name builder.TagMatchingRule(rule => { rule.TagName = tagHelper.TagMatchingRules.Single().TagName; rule.Attribute(attribute => { attribute.Name = "bind-" + valueAttribute.Name; attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; }); }); builder.BindAttribute(attribute => { attribute.Documentation = string.Format( Resources.BindTagHelper_Component_Documentation, valueAttribute.Name, changeAttribute.Name); attribute.Name = "bind-" + valueAttribute.Name; attribute.TypeName = valueAttribute.TypeName; attribute.IsEnum = valueAttribute.IsEnum; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName(valueAttribute.GetPropertyName()); }); results.Add(builder.Build()); } } return(results); }
public TagHelperServiceTestBase() { var builder1 = TagHelperDescriptorBuilder.Create("Test1TagHelper", "TestAssembly"); builder1.TagMatchingRule(rule => rule.TagName = "test1"); builder1.SetTypeName("Test1TagHelper"); builder1.BindAttribute(attribute => { attribute.Name = "bool-val"; attribute.SetPropertyName("BoolVal"); attribute.TypeName = typeof(bool).FullName; }); builder1.BindAttribute(attribute => { attribute.Name = "int-val"; attribute.SetPropertyName("IntVal"); attribute.TypeName = typeof(int).FullName; }); var builder2 = TagHelperDescriptorBuilder.Create("Test2TagHelper", "TestAssembly"); builder2.TagMatchingRule(rule => rule.TagName = "test2"); builder2.SetTypeName("Test2TagHelper"); builder2.BindAttribute(attribute => { attribute.Name = "bool-val"; attribute.SetPropertyName("BoolVal"); attribute.TypeName = typeof(bool).FullName; }); builder2.BindAttribute(attribute => { attribute.Name = "int-val"; attribute.SetPropertyName("IntVal"); attribute.TypeName = typeof(int).FullName; }); var builder3 = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Component1TagHelper", "TestAssembly"); builder3.TagMatchingRule(rule => rule.TagName = "Component1"); builder3.SetTypeName("Component1"); builder3.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch; builder3.BindAttribute(attribute => { attribute.Name = "bool-val"; attribute.SetPropertyName("BoolVal"); attribute.TypeName = typeof(bool).FullName; }); builder3.BindAttribute(attribute => { attribute.Name = "int-val"; attribute.SetPropertyName("IntVal"); attribute.TypeName = typeof(int).FullName; }); var directiveAttribute1 = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "TestDirectiveAttribute", "TestAssembly"); directiveAttribute1.TagMatchingRule(rule => { rule.TagName = "*"; rule.RequireAttributeDescriptor(b => { b.Name = "@test"; b.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch; }); }); directiveAttribute1.TagMatchingRule(rule => { rule.TagName = "*"; rule.RequireAttributeDescriptor(b => { b.Name = "@test"; b.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; }); }); directiveAttribute1.BindAttribute(attribute => { attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; attribute.Name = "@test"; attribute.SetPropertyName("Test"); attribute.TypeName = typeof(string).FullName; attribute.BindAttributeParameter(parameter => { parameter.Name = "something"; parameter.TypeName = typeof(string).FullName; parameter.SetPropertyName("Something"); }); }); directiveAttribute1.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly] = bool.TrueString; directiveAttribute1.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch; directiveAttribute1.SetTypeName("TestDirectiveAttribute"); var directiveAttribute2 = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "MinimizedDirectiveAttribute", "TestAssembly"); directiveAttribute2.TagMatchingRule(rule => { rule.TagName = "*"; rule.RequireAttributeDescriptor(b => { b.Name = "@minimized"; b.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch; }); }); directiveAttribute2.TagMatchingRule(rule => { rule.TagName = "*"; rule.RequireAttributeDescriptor(b => { b.Name = "@minimized"; b.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; }); }); directiveAttribute2.BindAttribute(attribute => { attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; attribute.Name = "@minimized"; attribute.SetPropertyName("Minimized"); attribute.TypeName = typeof(bool).FullName; attribute.BindAttributeParameter(parameter => { parameter.Name = "something"; parameter.TypeName = typeof(string).FullName; parameter.SetPropertyName("Something"); }); }); directiveAttribute2.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly] = bool.TrueString; directiveAttribute2.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch; directiveAttribute2.SetTypeName("TestDirectiveAttribute"); DefaultTagHelpers = new[] { builder1.Build(), builder2.Build(), builder3.Build(), directiveAttribute1.Build(), directiveAttribute2.Build() }; HtmlFactsService = new DefaultHtmlFactsService(); TagHelperFactsService = new DefaultTagHelperFactsService(); RazorTagHelperCompletionService = new DefaultRazorTagHelperCompletionService(TagHelperFactsService); }
public DefaultProjectSnapshotManagerTest() { // Force VB and C# to Load GC.KeepAlive(typeof(Microsoft.CodeAnalysis.CSharp.SyntaxFactory)); GC.KeepAlive(typeof(Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory)); TagHelperResolver = new TestTagHelperResolver(); Documents = new HostDocument[] { TestProjectData.SomeProjectFile1, TestProjectData.SomeProjectFile2, // linked file TestProjectData.AnotherProjectNestedFile3, TestProjectData.SomeProjectComponentFile1, TestProjectData.SomeProjectComponentFile2, }; HostProject = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_2_0); HostProjectWithConfigurationChange = new HostProject(TestProjectData.SomeProject.FilePath, FallbackRazorConfiguration.MVC_1_0); ProjectManager = new TestProjectSnapshotManager(Dispatcher, Enumerable.Empty <ProjectSnapshotChangeTrigger>(), Workspace); var projectId = ProjectId.CreateNewId("Test"); var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create( projectId, VersionStamp.Default, "Test", "Test", LanguageNames.CSharp, TestProjectData.SomeProject.FilePath)); WorkspaceProject = solution.GetProject(projectId); var vbProjectId = ProjectId.CreateNewId("VB"); solution = solution.AddProject(ProjectInfo.Create( vbProjectId, VersionStamp.Default, "VB", "VB", LanguageNames.VisualBasic, "VB.vbproj")); VBWorkspaceProject = solution.GetProject(vbProjectId); var projectWithoutFilePathId = ProjectId.CreateNewId("NoFile"); solution = solution.AddProject(ProjectInfo.Create( projectWithoutFilePathId, VersionStamp.Default, "NoFile", "NoFile", LanguageNames.CSharp)); WorkspaceProjectWithoutFilePath = solution.GetProject(projectWithoutFilePathId); // Approximates a project with multi-targeting var projectIdWithDifferentTfm = ProjectId.CreateNewId("TestWithDifferentTfm"); solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create( projectIdWithDifferentTfm, VersionStamp.Default, "Test (Different TFM)", "Test", LanguageNames.CSharp, TestProjectData.SomeProject.FilePath)); WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm); SomeTagHelpers = TagHelperResolver.TagHelpers; SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build()); SourceText = SourceText.From("Hello world"); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType != JsonToken.StartObject) { return(null); } var descriptor = JObject.Load(reader); var descriptorKind = descriptor[nameof(TagHelperDescriptor.Kind)].Value <string>(); var typeName = descriptor[nameof(TagHelperDescriptor.Name)].Value <string>(); var assemblyName = descriptor[nameof(TagHelperDescriptor.AssemblyName)].Value <string>(); var tagMatchingRules = descriptor[nameof(TagHelperDescriptor.TagMatchingRules)].Value <JArray>(); var boundAttributes = descriptor[nameof(TagHelperDescriptor.BoundAttributes)].Value <JArray>(); var childTags = descriptor[nameof(TagHelperDescriptor.AllowedChildTags)].Value <JArray>(); var documentation = descriptor[nameof(TagHelperDescriptor.Documentation)].Value <string>(); var tagOutputHint = descriptor[nameof(TagHelperDescriptor.TagOutputHint)].Value <string>(); var caseSensitive = descriptor[nameof(TagHelperDescriptor.CaseSensitive)].Value <bool>(); var diagnostics = descriptor[nameof(TagHelperDescriptor.Diagnostics)].Value <JArray>(); var metadata = descriptor[nameof(TagHelperDescriptor.Metadata)].Value <JObject>(); var builder = TagHelperDescriptorBuilder.Create(descriptorKind, typeName, assemblyName); builder.Documentation = documentation; builder.TagOutputHint = tagOutputHint; builder.CaseSensitive = caseSensitive; foreach (var tagMatchingRule in tagMatchingRules) { var rule = tagMatchingRule.Value <JObject>(); builder.TagMatchingRule(b => ReadTagMatchingRule(b, rule, serializer)); } foreach (var boundAttribute in boundAttributes) { var attribute = boundAttribute.Value <JObject>(); builder.BindAttribute(b => ReadBoundAttribute(b, attribute, serializer)); } foreach (var childTag in childTags) { var tag = childTag.Value <JObject>(); builder.AllowChildTag(childTagBuilder => ReadAllowedChildTag(childTagBuilder, tag, serializer)); } foreach (var diagnostic in diagnostics) { var diagnosticReader = diagnostic.CreateReader(); var diagnosticObject = serializer.Deserialize <RazorDiagnostic>(diagnosticReader); builder.Diagnostics.Add(diagnosticObject); } var metadataReader = metadata.CreateReader(); var metadataValue = serializer.Deserialize <Dictionary <string, string> >(metadataReader); foreach (var item in metadataValue) { builder.Metadata[item.Key] = item.Value; } return(builder.Build()); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType != JsonToken.StartObject) { return(null); } // Required tokens (order matters) var descriptorKind = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Kind)); var typeName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.Name)); var assemblyName = reader.ReadNextStringProperty(nameof(TagHelperDescriptor.AssemblyName)); var builder = TagHelperDescriptorBuilder.Create(descriptorKind, typeName, assemblyName); reader.ReadProperties(propertyName => { switch (propertyName) { case nameof(TagHelperDescriptor.Documentation): if (reader.Read()) { var documentation = (string)reader.Value; builder.Documentation = documentation; } break; case nameof(TagHelperDescriptor.TagOutputHint): if (reader.Read()) { var tagOutputHint = (string)reader.Value; builder.TagOutputHint = tagOutputHint; } break; case nameof(TagHelperDescriptor.CaseSensitive): if (reader.Read()) { var caseSensitive = (bool)reader.Value; builder.CaseSensitive = caseSensitive; } break; case nameof(TagHelperDescriptor.TagMatchingRules): ReadTagMatchingRules(reader, builder); break; case nameof(TagHelperDescriptor.BoundAttributes): ReadBoundAttributes(reader, builder); break; case nameof(TagHelperDescriptor.AllowedChildTags): ReadAllowedChildTags(reader, builder); break; case nameof(TagHelperDescriptor.Diagnostics): ReadDiagnostics(reader, builder.Diagnostics); break; case nameof(TagHelperDescriptor.Metadata): ReadMetadata(reader, builder.Metadata); break; } }); return(builder.Build()); }
public void ViewComponentTagHelperPass_Execute_CreatesViewComponentTagHelper() { // Arrange var codeDocument = CreateDocument(@" @addTagHelper TestTagHelper, TestAssembly <tagcloud foo=""17"">"); var tagHelpers = new[] { TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, "TestTagHelper", "TestAssembly") .TypeName("__Generated__TagCloudViewComponentTagHelper") .BoundAttributeDescriptor(attribute => attribute .Name("Foo") .TypeName("System.Int32") .PropertyName("Foo")) .TagMatchingRuleDescriptor(rule => rule.RequireTagName("tagcloud")) .AddMetadata(ViewComponentTagHelperMetadata.Name, "TagCloud") .Build() }; var engine = CreateEngine(tagHelpers); var pass = new ViewComponentTagHelperPass() { Engine = engine, }; var irDocument = CreateIRDocument(engine, codeDocument); var vcthFullName = "AspNetCore.test_cshtml.__Generated__TagCloudViewComponentTagHelper"; // Act pass.Execute(codeDocument, irDocument); // Assert var tagHelper = FindTagHelperNode(irDocument); Assert.Equal(vcthFullName, Assert.IsType<DefaultTagHelperCreateIntermediateNode>(tagHelper.Children[1]).TypeName); Assert.Equal("Foo", Assert.IsType<DefaultTagHelperPropertyIntermediateNode>(tagHelper.Children[2]).PropertyName); var @class = FindClassNode(irDocument); Assert.Equal(4, @class.Children.Count); var vcthClass = Assert.IsType<CSharpCodeIntermediateNode>(@class.Children.Last()); var tokenNode = vcthClass.Children[0] as IntermediateToken; Assert.Equal( @"[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute(""tagcloud"")] public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper { private readonly global::Microsoft.AspNetCore.Mvc.IViewComponentHelper _helper = null; public __Generated__TagCloudViewComponentTagHelper(global::Microsoft.AspNetCore.Mvc.IViewComponentHelper helper) { _helper = helper; } [Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute, global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewContextAttribute] public global::Microsoft.AspNetCore.Mvc.Rendering.ViewContext ViewContext { get; set; } public System.Int32 Foo { get; set; } public override async global::System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { (_helper as global::Microsoft.AspNetCore.Mvc.ViewFeatures.IViewContextAware)?.Contextualize(ViewContext); var content = await _helper.InvokeAsync(""TagCloud"", new { Foo }); output.TagName = null; output.Content.SetHtmlContent(content); } } ", tokenNode.Content, ignoreLineEndingDifferences: true); Assert.Equal(TokenKind.CSharp, tokenNode.Kind); }
public DefaultProjectSnapshotManagerTest() { TagHelperResolver = new TestTagHelperResolver(); HostServices = TestServices.Create( new IWorkspaceService[] { new TestProjectSnapshotProjectEngineFactory(), }, new ILanguageService[] { TagHelperResolver, }); Documents = new HostDocument[] { new HostDocument("c:\\MyProject\\File.cshtml", "File.cshtml"), new HostDocument("c:\\MyProject\\Index.cshtml", "Index.cshtml"), // linked file new HostDocument("c:\\SomeOtherProject\\Index.cshtml", "Pages\\Index.cshtml"), }; HostProject = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_2_0); HostProjectWithConfigurationChange = new HostProject("c:\\MyProject\\Test.csproj", FallbackRazorConfiguration.MVC_1_0); Workspace = TestWorkspace.Create(HostServices); ProjectManager = new TestProjectSnapshotManager(Dispatcher, Enumerable.Empty <ProjectSnapshotChangeTrigger>(), Workspace); var projectId = ProjectId.CreateNewId("Test"); var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create( projectId, VersionStamp.Default, "Test", "Test", LanguageNames.CSharp, "c:\\MyProject\\Test.csproj")); WorkspaceProject = solution.GetProject(projectId); var vbProjectId = ProjectId.CreateNewId("VB"); solution = solution.AddProject(ProjectInfo.Create( vbProjectId, VersionStamp.Default, "VB", "VB", LanguageNames.VisualBasic, "VB.vbproj")); VBWorkspaceProject = solution.GetProject(vbProjectId); var projectWithoutFilePathId = ProjectId.CreateNewId("NoFile"); solution = solution.AddProject(ProjectInfo.Create( projectWithoutFilePathId, VersionStamp.Default, "NoFile", "NoFile", LanguageNames.CSharp)); WorkspaceProjectWithoutFilePath = solution.GetProject(projectWithoutFilePathId); // Approximates a project with multi-targeting var projectIdWithDifferentTfm = ProjectId.CreateNewId("TestWithDifferentTfm"); solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create( projectIdWithDifferentTfm, VersionStamp.Default, "Test (Different TFM)", "Test", LanguageNames.CSharp, "c:\\MyProject\\Test.csproj")); WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm); SomeTagHelpers = TagHelperResolver.TagHelpers; SomeTagHelpers.Add(TagHelperDescriptorBuilder.Create("Test1", "TestAssembly").Build()); SourceText = SourceText.From("Hello world"); }
private List <TagHelperDescriptor> CreateEventHandlerTagHelpers(List <EventHandlerData> data) { var results = new List <TagHelperDescriptor>(); for (var i = 0; i < data.Count; i++) { var entry = data[i]; var attributeName = "@" + entry.Attribute; var eventArgType = entry.EventArgsType.ToDisplayString(); var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.EventHandler.TagHelperKind, entry.Attribute, ComponentsApi.AssemblyName); builder.CaseSensitive = true; builder.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.EventHandlerTagHelper_Documentation, attributeName, eventArgType); builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.EventHandler.TagHelperKind); builder.Metadata.Add(ComponentMetadata.EventHandler.EventArgsType, eventArgType); builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString); builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.EventHandler.RuntimeName; // WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the tooltips. builder.SetTypeName(entry.TypeName); builder.TagMatchingRule(rule => { rule.TagName = "*"; rule.Attribute(a => { a.Name = attributeName; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; a.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); if (entry.EnablePreventDefault) { builder.TagMatchingRule(rule => { rule.TagName = "*"; rule.Attribute(a => { a.Name = attributeName + ":preventDefault"; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; a.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); } if (entry.EnableStopPropagation) { builder.TagMatchingRule(rule => { rule.TagName = "*"; rule.Attribute(a => { a.Name = attributeName + ":stopPropagation"; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; a.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; }); }); } builder.BindAttribute(a => { a.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.EventHandlerTagHelper_Documentation, attributeName, eventArgType); a.Name = attributeName; // We want event handler directive attributes to default to C# context. a.TypeName = $"Microsoft.AspNetCore.Components.EventCallback<{eventArgType}>"; // But make this weakly typed (don't type check) - delegates have their own type-checking // logic that we don't want to interfere with. a.Metadata.Add(ComponentMetadata.Component.WeaklyTypedKey, bool.TrueString); a.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the tooltips. a.SetPropertyName(entry.Attribute); if (entry.EnablePreventDefault) { a.BindAttributeParameter(parameter => { parameter.Name = "preventDefault"; parameter.TypeName = typeof(bool).FullName; parameter.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.EventHandlerTagHelper_PreventDefault_Documentation, attributeName); parameter.SetPropertyName("PreventDefault"); }); } if (entry.EnableStopPropagation) { a.BindAttributeParameter(parameter => { parameter.Name = "stopPropagation"; parameter.TypeName = typeof(bool).FullName; parameter.Documentation = string.Format( CultureInfo.CurrentCulture, ComponentResources.EventHandlerTagHelper_StopPropagation_Documentation, attributeName); parameter.SetPropertyName("StopPropagation"); }); } }); results.Add(builder.Build()); } return(results); }
private List <TagHelperDescriptor> CreateElementBindTagHelpers(List <ElementBindData> data) { var results = new List <TagHelperDescriptor>(); for (var i = 0; i < data.Count; i++) { var entry = data[i]; var name = entry.Suffix == null ? "Bind" : "Bind_" + entry.Suffix; var attributeName = entry.Suffix == null ? "bind" : "bind-" + entry.Suffix; var formatName = entry.Suffix == null ? "Format_" + entry.ValueAttribute : "Format_" + entry.Suffix; var formatAttributeName = entry.Suffix == null ? "format-" + entry.ValueAttribute : "format-" + entry.Suffix; var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.Bind.TagHelperKind, name, entry.Assembly); builder.Documentation = string.Format( Resources.BindTagHelper_Element_Documentation, entry.ValueAttribute, entry.ChangeAttribute); builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind); builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName; builder.Metadata[BlazorMetadata.Bind.ValueAttribute] = entry.ValueAttribute; builder.Metadata[BlazorMetadata.Bind.ChangeAttribute] = entry.ChangeAttribute; if (entry.TypeAttribute != null) { // For entries that map to the <input /> element, we need to be able to know // the difference between <input /> and <input type="text" .../> for which we // want to use the same attributes. // // We provide a tag helper for <input /> that should match all input elements, // but we only want it to be used when a more specific one is used. // // Therefore we use this metadata to know which one is more specific when two // tag helpers match. builder.Metadata[BlazorMetadata.Bind.TypeAttribute] = entry.TypeAttribute; } // WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. builder.SetTypeName(entry.TypeName); builder.TagMatchingRule(rule => { rule.TagName = entry.Element; if (entry.TypeAttribute != null) { rule.Attribute(a => { a.Name = "type"; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; a.Value = entry.TypeAttribute; a.ValueComparisonMode = RequiredAttributeDescriptor.ValueComparisonMode.FullMatch; }); } rule.Attribute(a => { a.Name = attributeName; a.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch; }); }); builder.BindAttribute(a => { a.Documentation = string.Format( Resources.BindTagHelper_Element_Documentation, entry.ValueAttribute, entry.ChangeAttribute); a.Name = attributeName; a.TypeName = typeof(object).FullName; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. a.SetPropertyName(name); }); builder.BindAttribute(attribute => { attribute.Documentation = string.Format(Resources.BindTagHelper_Element_Format_Documentation, attributeName); attribute.Name = formatAttributeName; attribute.TypeName = "System.String"; // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like // a C# property will crash trying to create the toolips. attribute.SetPropertyName(formatName); }); results.Add(builder.Build()); } return(results); }