public static (bool uidExists, string uidValue) GetOrGenerateUid(this XamlElementProcessor source, string xamlElement, string attributeName) { var uidExists = source.TryGetAttribute(xamlElement, Attributes.Uid, AttributeType.Inline, out AttributeType _, out int _, out int _, out string uid); if (!uidExists) { // reuse `Name` or `x:Name` if exist if (source.TryGetAttribute(xamlElement, Attributes.Name, AttributeType.InlineOrElement, out AttributeType _, out int _, out int _, out string name)) { uid = name; } else { var elementName = XamlElementProcessor.GetElementName(xamlElement.AsSpan()); if (source.TryGetAttribute(xamlElement, attributeName, AttributeType.InlineOrElement, out _, out _, out _, out string value)) { uid = $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(value)}{elementName}"; uid = uid.RemoveAllWhitespace().RemoveNonAlphaNumerics(); } else { // This is just a large random number created to hopefully avoid collisions uid = $"{elementName}{new Random().Next(1001, 8999)}"; } } }
public static void CheckForHardCodedAttribute(this XamlElementProcessor source, string fileName, string elementName, string attributeName, AttributeType types, string descriptionFormat, string xamlElement, ITextSnapshot snapshot, int offset, string guidFallbackAttributeName, Guid elementIdentifier, TagList tags, List <TagSuppression> suppressions, ProjectType projType) { if (source.TryGetAttribute(xamlElement, attributeName, types, out AttributeType foundAttributeType, out int tbIndex, out int length, out string value)) { if (!string.IsNullOrWhiteSpace(value) && char.IsLetterOrDigit(value[0])) { var tagDeps = source.CreateBaseTagDependencies( new Span(offset + tbIndex, length), snapshot, fileName); var(uidExists, uidValue) = source.GetOrGenerateUid(xamlElement, guidFallbackAttributeName); var tag = new HardCodedStringTag(tagDeps, elementName, attributeName, projType) { AttributeType = foundAttributeType, Value = value, Description = descriptionFormat.WithParams(value), UidExists = uidExists, UidValue = uidValue, ElementGuid = elementIdentifier, }; tags.TryAdd(tag, xamlElement, suppressions); } } }
public static Dictionary <int, int> GetExclusions(string xaml, string elementName) { string elementOpen = $"<{elementName}"; string elementOpenSpace = $"<{elementName} "; string elementOpenComplete = $"<{elementName}>"; string elementClose = $"</{elementName}>"; var exclusions = new Dictionary <int, int>(); // This is the opening position of the next opening (here) or closing (when set subsequently) tag var tagOfInterestPos = xaml.Substring(elementOpen.Length).AsSpan().FirstIndexOf(elementOpenComplete, elementOpenSpace) + elementOpen.Length; // track the number of open tags seen so know when get to the corresponding closing one. var openings = 0; // Track this outside the loop as may have nesting. int startClosePos = 0; while (tagOfInterestPos > elementOpen.Length && tagOfInterestPos < xaml.Length) { // closing tags if (xaml.Substring(tagOfInterestPos, 2) == "</") { // Allow for having seen multiple openings before the closing if (openings == 1) { exclusions.Add(startClosePos + 1, tagOfInterestPos + elementClose.Length); openings = 0; } else { openings -= 1; } } else { // ignore self closing tags as nothing to exclude if (!XamlElementProcessor.IsSelfClosing(xaml.AsSpan(), tagOfInterestPos)) { // opening tag s if (openings <= 0) { startClosePos = xaml.IndexOf(">", tagOfInterestPos, StringComparison.Ordinal); openings = 1; } else { openings += 1; } } } // Find next opening or closing tag var nextOpening = xaml.Substring(tagOfInterestPos + elementOpen.Length).AsSpan().FirstIndexOf(elementOpenComplete, elementOpenSpace); if (nextOpening > -1) { nextOpening += tagOfInterestPos + elementOpen.Length; } var nextClosing = xaml.IndexOf(elementClose, tagOfInterestPos + elementOpen.Length, StringComparison.Ordinal); tagOfInterestPos = nextOpening > -1 && nextOpening < nextClosing ? nextOpening : nextClosing; } return(exclusions); }
public override void Process(string fileName, int offset, string xamlElement, string linePadding, ITextSnapshot snapshot, TagList tags, List <TagSuppression> suppressions = null) { const string gridOpenSpace = "<Grid "; const string gridOpenComplete = "<Grid>"; var endOfOpening = xamlElement.IndexOf(">", StringComparison.Ordinal) + 1; var firstNestedGrid = xamlElement.FirstIndexOf(gridOpenSpace, gridOpenComplete); var rowDefPos = xamlElement.IndexOf("<Grid.RowDefinitions", StringComparison.Ordinal); var colDefPos = xamlElement.IndexOf("<Grid.ColumnDefinitions", StringComparison.Ordinal); var gridIsSelfClosing = XamlElementProcessor.IsSelfClosing(xamlElement); var hasRowDef = false; if (rowDefPos > 0) { hasRowDef = firstNestedGrid <= 0 || rowDefPos < firstNestedGrid; } var hasColDef = false; if (colDefPos > 0) { hasColDef = firstNestedGrid <= 0 || colDefPos < firstNestedGrid; } var leftPad = linePadding.Contains("\t") ? linePadding + "\t" : linePadding + " "; var rowDefsClosingPos = -1; if (!hasRowDef) { var tag = new AddRowDefinitionsTag(new Span(offset, endOfOpening), snapshot, fileName, this.Logger) { InsertPosition = offset + endOfOpening, LeftPad = leftPad, GridNeedsExpanding = gridIsSelfClosing, }; tags.TryAdd(tag, xamlElement, suppressions); rowDefsClosingPos = xamlElement.IndexOf(">", StringComparison.Ordinal); } else { rowDefsClosingPos = xamlElement.IndexOf("</Grid.RowDefinitions", StringComparison.Ordinal); } var colDefsClosingPos = -1; if (!hasColDef) { var tag = new AddColumnDefinitionsTag(new Span(offset, endOfOpening), snapshot, fileName, this.Logger) { InsertPosition = offset + endOfOpening, LeftPad = leftPad, GridNeedsExpanding = gridIsSelfClosing, }; tags.TryAdd(tag, xamlElement, suppressions); colDefsClosingPos = xamlElement.IndexOf(">", StringComparison.Ordinal); } else { colDefsClosingPos = xamlElement.IndexOf("</Grid.ColumnDefinitions", StringComparison.Ordinal); } if (!hasRowDef && !hasColDef) { var tag = new AddRowAndColumnDefinitionsTag(new Span(offset, endOfOpening), snapshot, fileName, this.Logger) { InsertPosition = offset + endOfOpening, LeftPad = leftPad, GridNeedsExpanding = gridIsSelfClosing, }; tags.TryAdd(tag, xamlElement, suppressions); } const string rowDefStart = "<RowDefinition"; var rowDefsCount = 0; var toAdd = new List <InsertRowDefinitionTag>(); var rowDefIndex = xamlElement.IndexOf(rowDefStart, StringComparison.Ordinal); while (rowDefIndex >= 0) { var endPos = xamlElement.IndexOf('>', rowDefIndex); var tag = new InsertRowDefinitionTag(new Span(offset + rowDefIndex, endPos - rowDefIndex + 1), snapshot, fileName, this.Logger) { RowId = rowDefsCount, GridStartPos = offset, GridLength = xamlElement.Length, XamlTag = xamlElement.Substring(rowDefIndex, endPos - rowDefIndex + 1), InsertPoint = offset + rowDefIndex, }; rowDefsCount += 1; toAdd.Add(tag); rowDefIndex = xamlElement.IndexOf(rowDefStart, endPos, StringComparison.Ordinal); } foreach (var tag in toAdd) { tag.RowCount = rowDefsCount; tags.TryAdd(tag, xamlElement, suppressions); } const string colDef = "<ColumnDefinition"; var colDefsCount = 0; var colDefIndex = xamlElement.IndexOf(colDef, StringComparison.Ordinal); while (colDefIndex > -1) { colDefsCount += 1; colDefIndex = xamlElement.IndexOf(colDef, colDefIndex + 1, StringComparison.Ordinal); } const string rowDefUse = "Grid.Row=\""; const string colDefUse = "Grid.Column=\""; int highestAssignedRow = -1; int highestAssignedCol = -1; var undefinedTags = new List <MissingDefinitionTag>(); var nextDefUseIndex = xamlElement.FirstIndexOf(rowDefUse, colDefUse); var defUseOffset = 0; while (nextDefUseIndex > 0) { defUseOffset += nextDefUseIndex; if (nextDefUseIndex > endOfOpening) { if (!xamlElement.InComment(defUseOffset)) { // Get assigned value if (xamlElement.Substring(defUseOffset).StartsWith(rowDefUse)) { var valueStartPos = defUseOffset + rowDefUse.Length; var closePos = xamlElement.IndexOf("\"", valueStartPos, StringComparison.Ordinal); var assignedStr = xamlElement.Substring(valueStartPos, closePos - valueStartPos); if (int.TryParse(assignedStr, out int assignedInt)) { if (assignedInt > 0 && assignedInt >= rowDefsCount) { undefinedTags.Add(new MissingRowDefinitionTag( new Span(offset + defUseOffset, closePos - defUseOffset + 1), snapshot, fileName, this.Logger) { AssignedInt = assignedInt, Description = StringRes.UI_XamlAnalysisMissingRowDefinitionDescription.WithParams(assignedInt), ExistingDefsCount = rowDefsCount, HasSomeDefinitions = hasRowDef, InsertPosition = offset + rowDefsClosingPos, LeftPad = leftPad, }); } if (assignedInt > highestAssignedRow) { highestAssignedRow = assignedInt; } } } else if (xamlElement.Substring(defUseOffset).StartsWith(colDefUse)) { var valueStartPos = defUseOffset + colDefUse.Length; var closePos = xamlElement.IndexOf("\"", valueStartPos, StringComparison.Ordinal); var assignedStr = xamlElement.Substring(valueStartPos, closePos - valueStartPos); if (int.TryParse(assignedStr, out int assignedInt)) { if (assignedInt > 0 && assignedInt >= colDefsCount) { undefinedTags.Add(new MissingColumnDefinitionTag( new Span(offset + defUseOffset, closePos - defUseOffset + 1), snapshot, fileName, this.Logger) { AssignedInt = assignedInt, Description = StringRes.UI_XamlAnalysisMissingColumnDefinitionDescription.WithParams(assignedInt), ExistingDefsCount = colDefsCount, HasSomeDefinitions = hasColDef, InsertPosition = offset + colDefsClosingPos, LeftPad = leftPad, }); } if (assignedInt > highestAssignedCol) { highestAssignedCol = assignedInt; } } } } } nextDefUseIndex = xamlElement.Substring(defUseOffset + 1).FirstIndexOf(colDefUse, rowDefUse) + 1; } foreach (var undefinedTag in undefinedTags) { undefinedTag.TotalDefsRequired = undefinedTag is MissingRowDefinitionTag ? highestAssignedRow : highestAssignedCol; tags.TryAdd(undefinedTag, xamlElement, suppressions); } const string rowSpanUse = "Grid.RowSpan=\""; const string colSpanUse = "Grid.ColumnSpan=\""; var nextSpanUseIndex = xamlElement.FirstIndexOf(rowSpanUse, colSpanUse); var spanUseOffset = 0; while (nextSpanUseIndex > 0) { spanUseOffset += nextSpanUseIndex; if (nextSpanUseIndex > endOfOpening) { if (!xamlElement.InComment(spanUseOffset)) { if (xamlElement.Substring(spanUseOffset).StartsWith(rowSpanUse)) { var valueStartPos = spanUseOffset + rowSpanUse.Length; var closePos = xamlElement.IndexOf("\"", valueStartPos, StringComparison.Ordinal); var assignedStr = xamlElement.Substring(valueStartPos, closePos - valueStartPos); if (int.TryParse(assignedStr, out int assignedInt)) { var element = XamlElementProcessor.GetSubElementAtPosition(this.ProjectType, fileName, snapshot, xamlElement, spanUseOffset, this.Logger); var row = 0; if (this.TryGetAttribute(element, "Grid.Row", AttributeType.InlineOrElement, out _, out _, out _, out string rowStr)) { row = int.Parse(rowStr); } if (assignedInt > 1 && assignedInt - 1 + row >= rowDefsCount) { var rowTag = new RowSpanOverflowTag( new Span(offset + spanUseOffset, closePos - spanUseOffset + 1), snapshot, fileName, this.Logger) { TotalDefsRequired = assignedInt + row - 1, Description = StringRes.UI_XamlAnalysisRowSpanOverflowDescription, ExistingDefsCount = rowDefsCount, HasSomeDefinitions = hasRowDef, InsertPosition = offset + rowDefsClosingPos, LeftPad = leftPad, }; tags.TryAdd(rowTag, xamlElement, suppressions); } } } else if (xamlElement.Substring(spanUseOffset).StartsWith(colSpanUse)) { var valueStartPos = spanUseOffset + colSpanUse.Length; var closePos = xamlElement.IndexOf("\"", valueStartPos, StringComparison.Ordinal); var assignedStr = xamlElement.Substring(valueStartPos, closePos - valueStartPos); if (int.TryParse(assignedStr, out int assignedInt)) { var element = XamlElementProcessor.GetSubElementAtPosition(this.ProjectType, fileName, snapshot, xamlElement, spanUseOffset, this.Logger); var gridCol = 0; if (this.TryGetAttribute(element, "Grid.Column", AttributeType.InlineOrElement, out _, out _, out _, out string colStr)) { gridCol = int.Parse(colStr); } if (assignedInt > 1 && assignedInt - 1 + gridCol >= colDefsCount) { var colTag = new ColumnSpanOverflowTag( new Span(offset + spanUseOffset, closePos - spanUseOffset + 1), snapshot, fileName, this.Logger) { TotalDefsRequired = assignedInt - 1 + gridCol, Description = StringRes.UI_XamlAnalysisColumnSpanOverflowDescription, ExistingDefsCount = colDefsCount, HasSomeDefinitions = hasColDef, InsertPosition = offset + colDefsClosingPos, LeftPad = leftPad, }; tags.TryAdd(colTag, xamlElement, suppressions); } } } } } nextSpanUseIndex = xamlElement.Substring(spanUseOffset + 1).FirstIndexOf(colSpanUse, rowSpanUse) + 1; } }