From 09d437928facab552b4c9bf0e9e4c621667affa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Standa=20Luke=C5=A1?= Date: Thu, 19 Oct 2023 16:35:38 +0200 Subject: [PATCH] Suppress the uppercase html attribute warning if it must have a prefix When setting HTML attributes on HierarchyRepeater Item and Level capabilites, it's more readable to uppercase the first letter, e.g. ItemClass instead of Itemclass. However, this triggered the "Class dotvvm property doesn't exist" warning. --- .../ControlTree/ControlTreeResolverBase.cs | 10 ++++++++-- .../DefaultControlTreeResolverTests.cs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs b/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs index 2e135fcd03..0ff543d36f 100644 --- a/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs +++ b/src/Framework/Framework/Compilation/ControlTree/ControlTreeResolverBase.cs @@ -454,12 +454,18 @@ attribute.ValueNode is DothtmlValueBindingNode binding && { var pGroup = groupedProperty.PropertyGroup; var name = groupedProperty.GroupMemberName; - if (pGroup.Name.EndsWith("Attributes") && name.ToLowerInvariant() != name) + var prefix = attribute.AttributeName.Substring(0, attribute.AttributeName.Length - name.Length); + // If the HTML attribute is used with a prefix such as `Item`, it might be clearer if the first character is uppercased + // e.g. ItemClass reads better than Itemclass + // we supress the warning in such case + var allowFirstCharacterUppercase = prefix.Length > 0 && char.IsLetter(prefix[prefix.Length - 1]); + if (pGroup.Name.EndsWith("Attributes") && + name.Substring(allowFirstCharacterUppercase ? 1 : 0).ToLowerInvariant() != name.Substring(allowFirstCharacterUppercase ? 1 : 0)) { // properties with at most two typos var similarNameProperties = control.Metadata.AllProperties - .Where(p => StringSimilarity.DamerauLevenshteinDistance(p.Name.ToLowerInvariant(), name.ToLowerInvariant()) <= 2) + .Where(p => StringSimilarity.DamerauLevenshteinDistance(p.Name.ToLowerInvariant(), (prefix + name).ToLowerInvariant()) <= 2) .Select(p => p.Name) .ToArray(); var similarPropertyHelp = diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs index 6bd475a3ff..95b960469f 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs @@ -767,6 +767,25 @@ @viewModel System.Boolean Assert.AreEqual("HTML attribute name 'Visble' should not contain uppercase letters. Did you mean Visible, or another DotVVM property?", attribute3.AttributeNameNode.NodeWarnings.Single()); } + [TestMethod] + public void DefaultViewCompiler_NonExistenPropertyWarning_PrefixedGroup() + { + var markup = $@" +@viewModel System.Boolean + +"; + var repeater = ParseSource(markup) + .Content.SelectRecursively(c => c.Content) + .Single(c => c.Metadata.Type == typeof(HierarchyRepeater)); + + var elementNode = (DothtmlElementNode)repeater.DothtmlNode; + var attribute1 = elementNode.Attributes.Single(a => a.AttributeName == "ItemClass"); + var attribute2 = elementNode.Attributes.Single(a => a.AttributeName == "ItemIncludeInPage"); + + Assert.AreEqual(0, attribute1.AttributeNameNode.NodeWarnings.Count(), attribute1.AttributeNameNode.NodeWarnings.StringJoin(", ")); + Assert.AreEqual("HTML attribute name 'IncludeInPage' should not contain uppercase letters. Did you intent to use a DotVVM property instead?", XAssert.Single(attribute2.AttributeNameNode.NodeWarnings)); + } + [TestMethod] public void DefaultViewCompiler_UnsupportedCallSite_ResourceBinding_Warning() {