/// <summary>
        ///     Provide completions for the specified location.
        /// </summary>
        /// <param name="location">
        ///     The <see cref="XmlLocation"/> where completions are requested.
        /// </param>
        /// <param name="projectDocument">
        ///     The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>.
        /// </param>
        /// <param name="triggerCharacters">
        ///     The character(s), if any, that triggered completion.
        /// </param>
        /// <param name="cancellationToken">
        ///     A <see cref="CancellationToken"/> that can be used to cancel the operation.
        /// </param>
        /// <returns>
        ///     A <see cref="Task{TResult}"/> that resolves either a <see cref="CompletionList"/>s, or <c>null</c> if no completions are provided.
        /// </returns>
        public override async Task <CompletionList> ProvideCompletions(XmlLocation location, ProjectDocument projectDocument, string triggerCharacters, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (location == null)
            {
                throw new ArgumentNullException(nameof(location));
            }

            if (projectDocument == null)
            {
                throw new ArgumentNullException(nameof(projectDocument));
            }

            List <CompletionItem> completions = new List <CompletionItem>();

            using (await projectDocument.Lock.ReaderLockAsync())
            {
                XSAttribute conditionAttribute;
                if (!location.IsAttributeValue(out conditionAttribute) || conditionAttribute.Name != "Condition")
                {
                    return(null);
                }

                if (conditionAttribute.Element.ParentElement?.Name != "PropertyGroup")
                {
                    return(null);
                }

                LspModels.Range replaceRange = conditionAttribute.ValueRange.ToLsp();

                completions.Add(new CompletionItem
                {
                    Label         = "If not already defined",
                    Detail        = "Condition",
                    Documentation = "Only use this property if the property does not already have a value.",
                    TextEdit      = new TextEdit
                    {
                        NewText = $"'$({conditionAttribute.Element.Name})' == ''",
                        Range   = replaceRange
                    }
                });
            }

            if (completions.Count == 0)
            {
                return(null);
            }

            return(new CompletionList(completions, isIncomplete: false));
        }
        public void InAttributeValue(string testFileName, int line, int column, string expectedAttributeName)
        {
            Position testPosition = new Position(line, column);

            string            testXml   = LoadTestFile("TestFiles", testFileName + ".xml");
            TextPositions     positions = new TextPositions(testXml);
            XmlDocumentSyntax document  = Parser.ParseText(testXml);

            XmlLocator  locator = new XmlLocator(document, positions);
            XmlLocation result  = locator.Inspect(testPosition);

            Assert.NotNull(result);

            XSAttribute attribute;

            Assert.True(result.IsAttribute(out attribute), "IsAttribute");
            Assert.True(result.IsAttributeValue(), "IsAttributeValue");

            Assert.Equal(expectedAttributeName, attribute.Name);

            // TODO: Verify Parent, PreviousSibling, and NextSibling.
        }