public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (element.ContainsDescendant("TwoPaneView")) { var invalidDescendants = element.GetDescendants("TwoPaneView"); var result = AnalysisActions.EmptyList; foreach (var desc in invalidDescendants) { result.HighlightDescendantWithoutAction( RapidXamlErrorType.Error, code: "WinUI-2PV", description: "Do not put a TwoPaneView inside the pane of another TwoPaneview.", descendant: desc, moreInfoUrl: "https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/two-pane-view#dos-and-donts"); } return(result); } else { return(AnalysisActions.None); } }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!extraDetails.TryGet("framework", out ProjectFramework framework) || framework != ProjectFramework.Uwp) { return(AnalysisActions.None); } var result = AnalysisActions.EmptyList; foreach (var attribute in element.Attributes) { if (attribute.HasStringValue) { var attrValue = attribute.StringValue; if (attrValue.StartsWith("{Binding")) { result.RemoveAttribute(RapidXamlErrorType.Suggestion, "RXTPOC", $"Use 'x:Bind' rather than 'Binding' for '{attribute.Name}'.", "Change to 'x:Bind'", attribute) .AndAddAttribute(attribute.Name, attrValue.Replace("{Binding", "{x:Bind")); } } } return(result); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!extraDetails.TryGet("framework", out ProjectFramework framework) || (framework != ProjectFramework.Uwp && framework != ProjectFramework.Wpf)) { return(AnalysisActions.None); } var result = AnalysisActions.EmptyList; if (framework.Equals(ProjectFramework.Uwp)) { // TODO: Issue#163 - check for hard-coded string for Attributes.Content // Then remove CheckBoxProcessor, CheckBoxCheckedAndUncheckedEventsTag & MissingCheckBoxEvents } // If using one event, the recommendation is to use both var hasCheckedEvent = element.HasAttribute(Attributes.CheckedEvent); var hasuncheckedEvent = element.HasAttribute(Attributes.UncheckedEvent); if (hasCheckedEvent && !hasuncheckedEvent) { var existingCheckedName = element.Attributes.FirstOrDefault(a => a.Name == Attributes.CheckedEvent).StringValue; var newEventName = existingCheckedName.ToLowerInvariant().Contains("checked") ? existingCheckedName.Replace("Checked", "UnChecked").Replace("checked", "Unchecked") : "OnCheckBoxUnchecked"; result.AddAttribute( RapidXamlErrorType.Warning, "RXT401", StringRes.UI_XamlAnalysisCheckBoxCheckedAndUncheckedEventsDescription, StringRes.UI_XamlAnalysisCheckBoxCheckedAndUncheckedEventsToolTip, Attributes.UncheckedEvent, newEventName, StringRes.UI_XamlAnalysisCheckBoxCheckedAndUncheckedEventsExtendedMessage, "https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/checkbox#handle-click-and-checked-events"); } if (!hasCheckedEvent && hasuncheckedEvent) { var existingUnheckedName = element.Attributes.FirstOrDefault(a => a.Name == Attributes.UncheckedEvent).StringValue; var newEventName = existingUnheckedName.ToLowerInvariant().Contains("unchecked") ? existingUnheckedName.Replace("UnChecked", "Checked").Replace("unchecked", "checked") : "OnCheckBoxChecked"; result.AddAttribute( RapidXamlErrorType.Warning, "RXT401", StringRes.UI_XamlAnalysisCheckBoxCheckedAndUncheckedEventsDescription, StringRes.UI_XamlAnalysisCheckBoxCheckedAndUncheckedEventsToolTip, Attributes.UncheckedEvent, newEventName, StringRes.UI_XamlAnalysisCheckBoxCheckedAndUncheckedEventsExtendedMessage, "https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/checkbox#handle-click-and-checked-events"); } return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { // Don't do anything. // Knowing this is called is enough as this exists only to know // that the parsed file can be turned into a RapidXamlElement. return(AnalysisActions.None); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var result = AutoFixAnalysisActions.RenameElement("WebView2"); result.AndAddAttribute("Source", "https://rapidxaml.dev/"); return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var result = AutoFixAnalysisActions.RenameElement("newcontrols:WebView2"); result.AndAddXmlns("newcontrols", "https://somenewdomain/newcontrols"); return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { return(AnalysisActions.ReplaceElement( RapidXamlErrorType.Suggestion, "Test4", "Don't use `NewName` yet.", "Comment out NewName element", $"<!--{element.OriginalString}-->")); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var result = AnalysisActions.EmptyList; if (!element.Attributes.Any(a => a.Name == "Mode")) { result.HighlightWithoutAction(RapidXamlErrorType.Warning, "ISSUE364", $"Set binding mode."); } return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!element.ContainsAttribute("OnLoaded")) { return(AutoFixAnalysisActions.AddAttribute("OnLoaded", "OnLoadedEventHandler")); } else { return(AutoFixAnalysisActions.None); } }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (element.ContainsChild("TextBlock")) { return(AutoFixAnalysisActions.RemoveChild(element.Children.First(c => c.Name == "TextBlock"))); } else { return(AutoFixAnalysisActions.None); } }
public override void Process(string fileName, int offset, string xamlElement, string linePadding, ITextSnapshot snapshot, TagList tags, List <TagSuppression> suppressions = null) { var rxElement = RapidXamlElementExtractor.GetElement(xamlElement, offset); var details = new ExtraAnalysisDetails(fileName, ProjectFrameworkHelper.FromType(this.ProjectType)); var analysisActions = this.customProcessor.Analyze(rxElement, details); if (!analysisActions.IsNone) { foreach (var action in analysisActions.Actions) { var tagDeps = new CustomAnalysisTagDependencies { AnalyzedElement = rxElement, Action = action, ElementName = GetElementName(xamlElement), // Do this to get any xmlns ErrorCode = action.Code, ErrorType = TagErrorTypeCreator.FromCustomAnalysisErrorType(action.ErrorType), FileName = fileName, InsertPos = offset, Logger = this.Logger, Snapshot = snapshot, }; // Treat `NotReallyCustomAnalyzer` types as any other built-in type. // Track additional information about 3rd party custom analyzers. if (this.customProcessor is CustomAnalysis.NotReallyCustomAnalyzer) { tagDeps.CustomFeatureUsageValue = this.customProcessor.GetType().Name; } else { tagDeps.CustomFeatureUsageValue = $"{this.customProcessor} {action.Code}"; } if (action.Location == null) { // Add one to allow for opening angle bracket tagDeps.Span = new Span(offset + 1, tagDeps.ElementName.Length); // Highlight only the opening element name } else { tagDeps.Span = action.Location.ToSpanPlusStartPos(offset); } tags.TryAdd( new CustomAnalysisTag(tagDeps), xamlElement, suppressions); } } }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (extraDetails.TryGet("xmlns", out Dictionary <string, string> xmlns)) { this.Count = xmlns.Count(); this.Xmlns = xmlns; } else { this.Count = 0; } return(AnalysisActions.EmptyList); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { // If a namespace alias is defined use the same one for the replacement element name. var replacementName = element.Name.Contains(":") ? $"{element.Name.Split(':')[0]}:NewName" : "NewName"; return(AnalysisActions.RenameElement( RapidXamlErrorType.Error, "Test3", "OldName is deprecated. Use 'NewName' instead.", "Replace OldName with NewName", replacementName)); }
internal AnalysisActions GetActions <T>(string xaml, ProjectType projectType = ProjectType.Any) where T : ICustomAnalyzer { var sut = (T)Activator.CreateInstance(typeof(T)); var element = RapidXamlElementExtractor.GetElement(xaml); var details = new ExtraAnalysisDetails( "test.xaml", ProjectFrameworkHelper.FromType(projectType)); var result = sut.Analyze(element, details); return(result); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!extraDetails.TryGet("framework", out ProjectFramework framework) || framework != ProjectFramework.XamarinForms) { return(AnalysisActions.None); } return(AnalysisActions.HighlightWithoutAction( errorType: RapidXamlErrorType.Suggestion, code: "RXT330", description: "CollectionView is a more flexible, and performant alternative to ListView. Consider changing it.", moreInfoUrl: "https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/", descendant: element)); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!element.ContainsAttribute("Bar")) { return(AnalysisActions.AddAttribute( RapidXamlErrorType.Warning, code: "CRXT888", description: "Always specify the 'Bar' attribute", actionText: "Add missing 'Bar' attribute", addAttributeName: "Bar", addAttributeValue: "TODO-SET-THIS")); } else { return(AnalysisActions.None); } }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var firstChild = element.Children.FirstOrDefault(); if (firstChild != null) { return(AnalysisActions.RemoveChild( RapidXamlErrorType.Warning, "Test7", "First child can be removed", $"Remove {firstChild.Name}", firstChild)); } else { return(AnalysisActions.None); } }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var defaultAlias = "newns"; var nsvalue = "using:mynewnamespace"; var aliasToUse = defaultAlias; extraDetails.TryGet("xmlns", out Dictionary <string, string> xmlns); if (xmlns != null) { if (xmlns.ContainsKey(defaultAlias)) { if (xmlns[defaultAlias] == nsvalue) { // What would be added is already there return(AnalysisActions.EmptyList); } else { if (xmlns.ContainsValue(nsvalue)) { // It already exists with a different alias // In other scenarios, this might affect what is used in other operations } var suffix = 1; while (xmlns.ContainsKey(aliasToUse)) { aliasToUse = defaultAlias + suffix++.ToString(); } } } } return(AnalysisActions.AddXmlns( RapidXamlErrorType.Warning, "addns", $"add xmlns ({aliasToUse})", "add xmlns", aliasToUse, nsvalue)); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var result = AnalysisActions.EmptyList; foreach (var attribute in element.Attributes) { if (attribute.HasStringValue) { var attrValue = attribute.StringValue; if (attrValue.StartsWith("{Binding ") && !attrValue.Contains(" Mode=")) { result.HighlightAttributeWithoutAction(RapidXamlErrorType.Warning, "ISSUE364", $"Set binding mode for '{attribute.Name}'.", attribute); } } } return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var defaultAllias = "controls"; var xmlnamespace = "using:Microsoft.UI.Xaml.Controls"; var aliasToUse = defaultAllias; var addAlias = true; extraDetails.TryGet("xmlns", out Dictionary <string, string> xmlns); // Check to see if there is already an alias for the desired namespace var xns = xmlns.FirstOrDefault(x => x.Value == xmlnamespace); if (xns.Equals(default(KeyValuePair <string, string>))) { // Make the default alias unique (if already in use) by adding a number to the end var numericSuffix = 1; while (xmlns.ContainsKey(aliasToUse)) { aliasToUse = defaultAllias + numericSuffix++.ToString(); } } else { aliasToUse = xns.Key; addAlias = false; } // var result = AutoFixAnalysisActions.RenameElement($"{aliasToUse}:WebView2"); var result = AnalysisActions.RenameElement( RapidXamlErrorType.Warning, "WEBV2", "Replace WebView with, the new and improved, WebView2", "Replace WebView with WebView2", $"{aliasToUse}:WebView2"); if (addAlias) { result.AndAddXmlns(aliasToUse, xmlnamespace); } return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { // TODO: Implement this analyzer as per your needs. // More details at https://github.com/mrlacey/Rapid-XAML-Toolkit/blob/main/docs/custom-analysis.md if (element.ContainsAttribute("IsEnabled")) { return(AnalysisActions.None); } else { return(AnalysisActions.AddAttribute( errorType: RapidXamlErrorType.Warning, code: "XMPL01", description: "Always set the 'IsEnabled' property on 'MyElement'.", actionText: "Add 'IsEnabled' attribute", addAttributeName: "IsEnabled", addAttributeValue: "True" )); } }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (element.Children.Count % 2 == 1) { return(AnalysisActions.AddChildString( RapidXamlErrorType.Suggestion, "Test5", "Add more children.", "Add child element", "<Child />")); } else { return(AnalysisActions.AddChild( RapidXamlErrorType.Suggestion, "Test5", "Add more children.", "Add child element", "Child", new List <(string name, string value)>(new[] { ("Name", $"Junior-{element.Children.Count}") })));
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { var ttlAttr = element.GetAttributes(Attributes.Title).FirstOrDefault(); if (ttlAttr != null && ttlAttr.HasStringValue) { var value = ttlAttr.StringValue; // TODO: ISSUE#163 change this to an RXT200 when can handle localization of Xamarin.Forms apps if (!string.IsNullOrWhiteSpace(value) && char.IsLetterOrDigit(value[0])) { return(AnalysisActions.HighlightAttributeWithoutAction( errorType: RapidXamlErrorType.Warning, code: "RXT201", description: StringRes.UI_XamlAnalysisGenericHardCodedStringDescription.WithParams(Elements.Picker, Attributes.Title, value), attribute: ttlAttr)); } } return(AnalysisActions.None); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!extraDetails.TryGet("framework", out ProjectFramework framework) || framework != ProjectFramework.Uwp) { return(AnalysisActions.None); } var result = AnalysisActions.EmptyList; var ignorable = element.Attributes.FirstOrDefault(a => a.Name == "mc:Ignorable"); this.CheckForXmlns("xamarin", element, ignorable, ref result); this.CheckForXmlns("not_win", element, ignorable, ref result); this.CheckForXmlns("android", element, ignorable, ref result); this.CheckForXmlns("ios", element, ignorable, ref result); this.CheckForXmlns("wasm", element, ignorable, ref result); this.CheckForXmlns("macos", element, ignorable, ref result); return(result); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { AnalysisActions result = AnalysisActions.None; var txtAttr = element.GetAttributes(Attributes.Text).FirstOrDefault(); if (txtAttr != null && txtAttr.HasStringValue) { var value = txtAttr.StringValue; // TODO: ISSUE#163 change this to an RXT200 when can handle localization of Xamarin.Forms apps if (!string.IsNullOrWhiteSpace(value) && char.IsLetterOrDigit(value[0])) { result.HighlightAttributeWithoutAction( errorType: RapidXamlErrorType.Warning, code: "RXT201", description: StringRes.UI_XamlAnalysisGenericHardCodedStringDescription.WithParams(Elements.EntryCell, Attributes.Text, value), attribute: txtAttr); } } var phAttr = element.GetAttributes("Placeholder").FirstOrDefault(); if (phAttr != null && phAttr.HasStringValue) { var value = phAttr.StringValue; // TODO: ISSUE#163 change this to an RXT200 when can handle localization of Xamarin.Forms apps if (!string.IsNullOrWhiteSpace(value) && char.IsLetterOrDigit(value[0])) { result.HighlightAttributeWithoutAction( errorType: RapidXamlErrorType.Warning, code: "RXT201", description: "Entry contains hard-coded Placeholder value '{0}'.".WithParams(value), attribute: phAttr); } } return(result); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { // Don't report anything if the source hasn't been set. // Allow for multiple possible values that could be used by accesibility tools. if (element.HasAttribute(Attributes.Source) && !element.HasAttribute(Attributes.AutomationId) && !element.HasAttribute(Attributes.APName) && !element.HasAttribute(Attributes.APHelpText) && !element.HasAttribute(Attributes.APLabeledBy)) { return(AnalysisActions.AddAttribute( RapidXamlErrorType.Warning, code: "RXT351", description: StringRes.UI_XamlAnalysisImageButtonAccessibilityDescription, actionText: StringRes.UI_UndoContextAddAutomationDescription, addAttributeName: Attributes.APName, addAttributeValue: "Set this to something meaningful")); } else { return(AnalysisActions.None); } }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { if (!extraDetails.TryGet("framework", out ProjectFramework framework) || framework != ProjectFramework.XamarinForms) { return(AnalysisActions.None); } // Don't report anything if the source hasn't been set. // Allow for multiple possible values that could be used by accesibility tools. if (element.HasAttribute(Attributes.Source) && !element.HasAttribute(Attributes.AutomationId) && !element.HasAttribute(Attributes.APName) && !element.HasAttribute(Attributes.APHelpText) && !element.HasAttribute(Attributes.APLabeledBy)) { if (!element.TryGetAttributeStringValue(Attributes.APIsInAccessibleTree, out string inTree) || inTree.Equals("true", System.StringComparison.InvariantCultureIgnoreCase)) { return(AnalysisActions.AddAttribute( RapidXamlErrorType.Warning, code: "RXT350", description: StringRes.UI_XamlAnalysisImageAccessibilityDescription, actionText: StringRes.UI_UndoContextAddAutomationDescription, addAttributeName: Attributes.APName, addAttributeValue: "Set this to something meaningful", moreInfoUrl: null, extendedMessage: StringRes.UI_XamlAnalysisImageAccessibilityExtendedMessage) .OrAddAttribute( actionText: StringRes.UI_UndoContextExcludeFromAutomation, addAttributeName: Attributes.APIsInAccessibleTree, addAttributeValue: "false")); } } return(AnalysisActions.None); }
public override AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { AnalysisActions result = AnalysisActions.None; if (!element.HasAttribute(Attributes.Keyboard)) { string nonDefaultSuggestion = null; var xaml = element.OriginalString.ToLowerInvariant(); if (xaml.Contains("email")) { nonDefaultSuggestion = "Email"; } else if (xaml.Contains("phone") || xaml.Contains("cell") || xaml.Contains("mobile")) { nonDefaultSuggestion = "Telephone"; } else if (xaml.Contains("url")) { nonDefaultSuggestion = "Url"; } result.AddAttribute( RapidXamlErrorType.Suggestion, "RXT300", description: StringRes.UI_XamlAnalysisEntryWithoutKeyboardDescription, actionText: StringRes.UI_UndoContextAddEntryKeyboard, addAttributeName: Attributes.Keyboard, addAttributeValue: "Default", extendedMessage: StringRes.UI_XamlAnalysisEntryWithoutKeyboardExtendedMessage); if (!string.IsNullOrEmpty(nonDefaultSuggestion)) { result.OrAddAttribute( actionText: StringRes.UI_AddEntryKeyboard.WithParams(nonDefaultSuggestion), addAttributeName: Attributes.Keyboard, addAttributeValue: nonDefaultSuggestion); } } var txtAttr = element.GetAttributes(Attributes.Text).FirstOrDefault(); if (txtAttr != null && txtAttr.HasStringValue) { var value = txtAttr.StringValue; // TODO: ISSUE#163 change this to an RXT200 when can handle localization of Xamarin.Forms apps if (!string.IsNullOrWhiteSpace(value) && char.IsLetterOrDigit(value[0])) { result.HighlightAttributeWithoutAction( errorType: RapidXamlErrorType.Warning, code: "RXT201", description: StringRes.UI_XamlAnalysisGenericHardCodedStringDescription.WithParams(Elements.Entry, Attributes.Text, value), attribute: txtAttr); } } var phAttr = element.GetAttributes(Attributes.Placeholder).FirstOrDefault(); if (phAttr != null && phAttr.HasStringValue) { var value = phAttr.StringValue; // TODO: ISSUE#163 change this to an RXT200 when can handle localization of Xamarin.Forms apps if (!string.IsNullOrWhiteSpace(value) && char.IsLetterOrDigit(value[0])) { result.HighlightAttributeWithoutAction( errorType: RapidXamlErrorType.Warning, code: "RXT201", description: StringRes.UI_XamlAnalysisGenericHardCodedStringDescription.WithParams(Elements.Entry, Attributes.Placeholder, value), attribute: phAttr); } } var isPwdAttr = element.GetAttributes(Attributes.IsPassword).FirstOrDefault(); if (isPwdAttr != null && isPwdAttr.HasStringValue) { var value = isPwdAttr.StringValue; if (value.Equals("true", StringComparison.InvariantCultureIgnoreCase)) { if (!element.ContainsAttribute(Attributes.MaxLength)) { result.AddAttribute( errorType: RapidXamlErrorType.Suggestion, code: "RXT301", description: StringRes.UI_XamlAnalysisPasswordWithoutMaxLengthDescription, extendedMessage: StringRes.UI_XamlAnalysisPasswordWithoutMaxLengthExtendedMessage, actionText: StringRes.UI_UndoContextAddMaxLangthProperty, addAttributeName: Attributes.MaxLength, addAttributeValue: "100"); } } } return(result); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { AnalyzeCallCount += 1; return(AnalysisActions.None); }
public AnalysisActions Analyze(RapidXamlElement element, ExtraAnalysisDetails extraDetails) { return(AutoFixAnalysisActions.RenameElement("WebView2")); }