/// <summary> /// Add an unresolved SDK-style import (i.e. condition is false). /// </summary> /// <param name="import"> /// The declaring import element. /// </param> void AddUnresolvedSdkImport(ProjectImportElement import) { if (import == null) { throw new ArgumentNullException(nameof(import)); } Position importStart = import.Location.ToNative(); XmlLocation importLocation = _projectXmlLocator.Inspect(importStart); if (importLocation == null) { return; } XSElement importElement; if (!importLocation.IsElement(out importElement)) { return; } XSAttribute sdkAttribute = importElement["Sdk"]; Add( new MSBuildUnresolvedSdkImport(import, sdkAttribute) ); }
/// <summary> /// Does the location represent a place where an attribute value can be created or replaced by a completion? /// </summary> /// <param name="location"> /// The XML location. /// </param> /// <param name="targetAttribute"> /// The attribute (if any) whose value will be replaced by the completion. /// </param> /// <param name="onElementWithPath"> /// If specified, attribute's element must have the specified path. /// </param> /// <param name="forAttributeNamed"> /// If specified, the attribute must have one of the specified names. /// </param> /// <returns> /// <c>true</c>, if the location represents an attribute whose value can be replaced by a completion; otherwise, <c>false</c>. /// </returns> public static bool CanCompleteAttributeValue(this XmlLocation location, out XSAttribute targetAttribute, XSPath onElementWithPath = null, params string[] forAttributeNamed) { if (location == null) { throw new ArgumentNullException(nameof(location)); } targetAttribute = null; XSAttribute attribute; if (!location.IsAttributeValue(out attribute)) { return(false); } if (onElementWithPath != null && !attribute.HasParentPath(onElementWithPath)) { return(false); } if (forAttributeNamed.Length > 0 && Array.IndexOf(forAttributeNamed, attribute.Name) == -1) { return(false); } targetAttribute = attribute; return(true); }
/// <summary> /// Does the location represent an attribute's value? /// </summary> /// <param name="location"> /// The XML location. /// </param> /// <param name="attribute"> /// Receives the attribute whose value is represented by the location. /// </param> /// <returns> /// <c>true</c>, if the location represents an attribute's name; otherwise, <c>false</c>. /// </returns> public static bool IsAttributeValue(this XmlLocation location, out XSAttribute attribute) { if (location.IsAttributeValue()) { attribute = (XSAttribute)location.Node; return(true); } attribute = null; return(false); }
/// <summary> /// Does the location represent an attribute? /// </summary> /// <param name="location"> /// The XML location. /// </param> /// <param name="attribute"> /// Receives the <see cref="XSAttribute"/>. /// </param> /// <returns> /// <c>true</c>, if the location represents an attribute; otherwise, <c>false</c>. /// </returns> public static bool IsAttribute(this XmlLocation location, out XSAttribute attribute) { if (location == null) { throw new ArgumentNullException(nameof(location)); } if (!location.IsAttribute()) { attribute = null; return(false); } attribute = (XSAttribute)location.Node; return(true); }
/// <summary> /// Add an SDK-style import. /// </summary> /// <param name="resolvedImports"> /// The resolved imports resulting from the import declaration. /// </param> void AddSdkImport(IEnumerable <ResolvedImport> resolvedImports) { if (resolvedImports == null) { throw new ArgumentNullException(nameof(resolvedImports)); } ResolvedImport firstImport = resolvedImports.First(); Position importStart = firstImport.ImportingElement.Location.ToNative(); // If the Sdk attribute is on the Project element rather than an import element, then the location reported by MSBuild will be invalid (go figure). if (importStart == Position.Invalid) { importStart = Position.Origin; } XmlLocation importLocation = _projectXmlLocator.Inspect(importStart); if (importLocation == null) { return; } XSElement importElement; if (!importLocation.IsElement(out importElement)) { return; } XSAttribute sdkAttribute = importElement["Sdk"]; if (sdkAttribute == null) { return; } Add( new MSBuildSdkImport(resolvedImports.ToArray(), sdkAttribute) ); }
/// <summary> /// Visit an <see cref="XmlAttributeSyntax"/>. /// </summary> /// <param name="attribute"> /// The <see cref="XmlAttributeSyntax"/>. /// </param> /// <returns> /// The <see cref="XmlAttributeSyntax"/> (unchanged). /// </returns> public override SyntaxNode VisitXmlAttribute(XmlAttributeSyntax attribute) { if (!HaveCurrentElement) { return(attribute); } Range attributeRange = attribute.Span.ToNative(_textPositions); Range nameRange = attribute.NameNode?.Span.ToNative(_textPositions) ?? attributeRange; Range valueRange = attribute.ValueNode?.Span.ToNative(_textPositions) ?? Range.Zero; if (valueRange != Range.Zero && valueRange.End.ColumnNumber - valueRange.Start.ColumnNumber >= 2) { valueRange = valueRange.Transform(moveStartColumns: 1, moveEndColumns: -1); // Trim off quotes. } else { valueRange = nameRange; } XSAttribute xsAttribute; if (String.IsNullOrWhiteSpace(attribute.Name) || nameRange == attributeRange || valueRange == attributeRange) { xsAttribute = new XSInvalidAttribute(attribute, CurrentElement, attributeRange, nameRange, valueRange); } else { xsAttribute = new XSAttribute(attribute, CurrentElement, attributeRange, nameRange, valueRange); } CurrentElement.Attributes = CurrentElement.Attributes.Add(xsAttribute); DiscoveredNodes.Add(xsAttribute); return(attribute); }
/// <summary> /// Does the location represent a place where an attribute can be created or replaced by a completion? /// </summary> /// <param name="location"> /// The XML location. /// </param> /// <param name="element"> /// The element whose attribute will be completed. /// </param> /// <param name="replaceAttribute"> /// The attribute (if any) that will be replaced by the completion. /// </param> /// <param name="needsPadding"> /// An <see cref="PaddingType"/> value indicating what sort of padding (if any) is required before / after the attribute. /// </param> /// <param name="onElementWithPath"> /// If specified, the location's element must have the specified path. /// </param> /// <returns> /// <c>true</c>, if the location represents an element that can be replaced by completion; otherwise, <c>false</c>. /// </returns> public static bool CanCompleteAttribute(this XmlLocation location, out XSElement element, out XSAttribute replaceAttribute, out PaddingType needsPadding, XSPath onElementWithPath = null) { if (location == null) { throw new ArgumentNullException(nameof(location)); } replaceAttribute = null; needsPadding = PaddingType.None; XSAttribute attribute; if (location.IsAttribute(out attribute) && !location.IsValue()) { element = attribute.Element; if (location.Position == attribute.Start) { // Since we're on an existing attribute, we'll add a new attribute after it. attribute = null; needsPadding = PaddingType.Trailing; } } else if (location.IsElementBetweenAttributes(out element)) { if (element.Attributes.Count > 0) { // Check if we're directly before an attribute. foreach (XSAttribute currentAttribute in element.Attributes) { if (location.Position == currentAttribute.End) { needsPadding = PaddingType.Leading; } else { continue; } break; } } else if (location.Position == element.NameRange.End) // We're directly after the name. { needsPadding = PaddingType.Leading; } } else if (location.IsElement(out element)) { // Check if we're directly after the name. if (location.Position != element.NameRange.End) { return(false); } needsPadding = PaddingType.Leading; } else { return(false); } if (onElementWithPath != null && !element.Path.Matches(onElementWithPath)) { return(false); } replaceAttribute = attribute; return(true); }
/// <summary> /// Does the location represent an element that has the specified attribute? /// </summary> /// <param name="location"> /// The XML location. /// </param> /// <param name="attributeName"> /// The attribute name. /// </param> /// <param name="attribute"> /// Receives the attribute. /// </param> /// <returns> /// <c>true</c>, if the location represents an element with the specified attribute; otherwise, <c>false</c>. /// </returns> public static bool HasAttribute(this XmlLocation location, string attributeName, out XSAttribute attribute) { if (location.IsElement(out XSElement element)) { attribute = element[attributeName]; return(attribute != null); } attribute = null; return(false); }
/// <summary> /// Create a new <see cref="MSBuildImport"/>. /// </summary> /// <param name="imports"> /// A read-only list of underlying MSBuild <see cref="ResolvedImport"/>s representing the imports resulting from the SDK import. /// </param> /// <param name="sdkAttribute"> /// An <see cref="XSAttribute"/> representing the import's "Sdk" attribute. /// </param> public MSBuildSdkImport(IReadOnlyList <ResolvedImport> imports, XSAttribute sdkAttribute) : base(imports, sdkAttribute) { }
/// <summary> /// Create a new <see cref="MSBuildUnresolvedSdkImport"/>. /// </summary> /// <param name="import"> /// The underlying MSBuild <see cref="ProjectImportElement"/>. /// </param> /// <param name="sdkAttribute"> /// An <see cref="XSAttribute"/> representing the import's "Sdk" attribute. /// </param> public MSBuildUnresolvedSdkImport(ProjectImportElement import, XSAttribute sdkAttribute) : base(import, sdkAttribute) { }