Пример #1
0
        /// <summary>
        ///     Add a regular-style import.
        /// </summary>
        /// <param name="resolvedImports">
        ///     The resolved imports resulting from the import declaration.
        /// </param>
        void AddImport(IEnumerable <ResolvedImport> resolvedImports)
        {
            if (resolvedImports == null)
            {
                throw new ArgumentNullException(nameof(resolvedImports));
            }

            var importsByImportingElement = resolvedImports.GroupBy(import => import.ImportingElement);

            foreach (var importsForImportingElement in importsByImportingElement)
            {
                Position importStart = importsForImportingElement.Key.Location.ToNative();

                XmlLocation importLocation = _projectXmlLocator.Inspect(importStart);
                if (importLocation == null)
                {
                    continue;
                }

                XSElement importElement;
                if (!importLocation.IsElement(out importElement))
                {
                    continue;
                }

                Add(
                    new MSBuildImport(importsForImportingElement.ToArray(), importElement)
                    );
            }
        }
Пример #2
0
        /// <summary>
        ///     Add an unresolved regular-style import (i.e. condition is false).
        /// </summary>
        /// <param name="import">
        ///     The declaring import element.
        /// </param>
        void AddUnresolvedImport(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;
            }

            Add(
                new MSBuildUnresolvedImport(import, importElement)
                );
        }
Пример #3
0
        /// <summary>
        ///     Add a target.
        /// </summary>
        /// <param name="target">
        ///     The target's declaring <see cref="ProjectTargetElement"/>.
        /// </param>
        void AddTarget(ProjectTargetElement target)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            XmlLocation targetLocation = _projectXmlLocator.Inspect(
                target.Location.ToNative()
                );

            if (targetLocation == null)
            {
                return;
            }

            XSElement targetElement;

            if (!targetLocation.IsElement(out targetElement))
            {
                return;
            }

            Add(
                new MSBuildTarget(target, targetElement)
                );
        }
Пример #4
0
        /// <summary>
        ///     Does the location represent a place where an element can be created or replaced by a completion?
        /// </summary>
        /// <param name="location">
        ///     The XML location.
        /// </param>
        /// <param name="replaceElement">
        ///     The element (if any) that will be replaced by the completion.
        /// </param>
        /// <param name="parentPath">
        ///     If specified, the location's node's parent must have the specified <see cref="XSPath"/>.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the location represents an element that can be replaced by completion; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>
        ///     We can replace "&lt;&gt;" and "&lt;&lt;Element /&gt;".
        /// </remarks>
        public static bool CanCompleteElement(this XmlLocation location, out XSElement replaceElement, XSPath parentPath = null)
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            replaceElement = null;

            // Simplest case - we're on whitespace so we can simply insert an element without replacing anything.
            if (location.IsWhitespace(out XSWhitespace whitespace) && (parentPath == null || whitespace.HasParentPath(parentPath)))
            {
                return(true);
            }

            XSElement element;

            if (!location.IsElement(out element))
            {
                return(false);
            }

            if (location.IsElementBetweenAttributes())
            {
                return(false);
            }

            // Check if we need to perform a substition of the element to be replaced (the common case is simply replacing an existing element or partial element).
            if (element.IsValid)
            {
                // Do we have an invalid parent (e.g. "<<Foo />", which yields invalid element named "" with child element named "Foo")?
                bool isParentValid = element.ParentElement?.IsValid ?? true;
                if (!isParentValid)
                {
                    // We can't handle the case where the parent isn't on the same line as the child (since that's not the case outlined above).
                    if (element.ParentElement.Start.LineNumber != location.Node.Start.LineNumber)
                    {
                        return(false);
                    }

                    // But we *can* handle this case by targeting the "parent" element (since that's the element we're actually after anyway).
                    if (location.Node.Start.ColumnNumber - element.ParentElement.Start.ColumnNumber == 1)
                    {
                        element = element.ParentElement;
                    }
                }
            }

            if (parentPath != null && !element.HasParentPath(parentPath))
            {
                return(false);
            }

            replaceElement = element;

            return(true);
        }
Пример #5
0
        /// <summary>
        ///     Does the location represent an element or an attribute?
        /// </summary>
        /// <param name="location">
        ///     The XML location.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the location represents an element or attribute; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsElementOrAttribute(this XmlLocation location)
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            return(location.IsElement() || location.IsAttribute());
        }
Пример #6
0
        /// <summary>
        ///     Does the location represent an element's attributes range (but not a specific attribute)?
        /// </summary>
        /// <param name="location">
        ///     The XML location.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the location represents an element; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsElementBetweenAttributes(this XmlLocation location)
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            return(location.IsElement() && location.Flags.HasFlag(XmlLocationFlags.Attributes));
        }
Пример #7
0
        /// <summary>
        ///     Does the location represent an element's closing tag?
        /// </summary>
        /// <param name="location">
        ///     The XML location.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the location represents an element's closing tag; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsElementClosingTag(this XmlLocation location)
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            return(location.IsElement() && location.Flags.HasFlag(XmlLocationFlags.ClosingTag));
        }
Пример #8
0
        /// <summary>
        ///     Does the location represent an element's textual content?
        /// </summary>
        /// <param name="location">
        ///     The XML location.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the location represents an element's textual content; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsElementText(this XmlLocation location)
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            return(location.IsElement() && location.IsText());
        }
Пример #9
0
        /// <summary>
        ///     Does the location represent an element?
        /// </summary>
        /// <param name="location">
        ///     The XML location.
        /// </param>
        /// <param name="element">
        ///     Receives the <see cref="XSElement"/>.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the location represents an element; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsElement(this XmlLocation location, out XSElement element)
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            if (location.IsElement() && !location.IsElementContent())
            {
                element = (XSElement)location.Node;

                return(true);
            }
            else
            {
                element = null;

                return(false);
            }
        }
Пример #10
0
        /// <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)
                );
        }
Пример #11
0
        /// <summary>
        ///     Add a property.
        /// </summary>
        /// <param name="property">
        ///     The property's declaring <see cref="ProjectPropertyElement"/>.
        /// </param>
        void AddProperty(ProjectPropertyElement property)
        {
            if (property == null)
            {
                throw new ArgumentNullException(nameof(property));
            }

            XmlLocation propertyLocation = _projectXmlLocator.Inspect(
                property.Location.ToNative()
                );

            if (propertyLocation == null)
            {
                return;
            }

            XSElement propertyElement;

            if (!propertyLocation.IsElement(out propertyElement))
            {
                return;
            }

            ProjectProperty evaluatedProperty = _project.GetProperty(property.Name);

            if (evaluatedProperty != null)
            {
                Add(
                    new MSBuildProperty(evaluatedProperty, property, propertyElement)
                    );
            }
            else
            {
                Add(
                    new MSBuildUnusedProperty(property, propertyElement)
                    );
            }
        }
Пример #12
0
        /// <summary>
        ///     Add all items defined in the project.
        /// </summary>
        void AddItems()
        {
            // First, map each item to the element in the XML from where it originates.
            var itemsByXml = new Dictionary <ProjectItemElement, List <ProjectItem> >();

            foreach (ProjectItem item in _project.ItemsIgnoringCondition)
            {
                // Must be declared in main project file.
                if (!IsFromCurrentProject(item.Xml))
                {
                    continue;
                }

                Position itemStartPosition = item.Xml.Location.ToNative();

                List <ProjectItem> itemsFromXml;
                if (!itemsByXml.TryGetValue(item.Xml, out itemsFromXml))
                {
                    itemsFromXml = new List <ProjectItem>();
                    itemsByXml.Add(item.Xml, itemsFromXml);
                }

                itemsFromXml.Add(item);
            }

            // Now process item elements and their associated items.
            HashSet <ProjectItem> usedItems = new HashSet <ProjectItem>(_project.Items);

            foreach (ProjectItemElement itemXml in itemsByXml.Keys)
            {
                Position itemStart = itemXml.Location.ToNative();

                XmlLocation itemLocation = _projectXmlLocator.Inspect(itemStart);
                if (itemLocation == null)
                {
                    continue;
                }

                XSElement itemElement;
                if (!itemLocation.IsElement(out itemElement))
                {
                    continue;
                }

                List <ProjectItem> itemsFromXml;
                if (!itemsByXml.TryGetValue(itemXml, out itemsFromXml)) // AF: Should not happen.
                {
                    throw new InvalidOperationException($"Found item XML at {itemLocation.Node.Range} with no corresponding items in the MSBuild project (irrespective of condition).");
                }

                if (usedItems.Contains(itemsFromXml[0]))
                {
                    Add(
                        new MSBuildItemGroup(itemsByXml[itemXml], itemXml, itemElement)
                        );
                }
                else
                {
                    Add(
                        new MSBuildUnusedItemGroup(itemsByXml[itemXml], itemXml, itemElement)
                        );
                }
            }
        }
Пример #13
0
        /// <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);
        }