/// <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="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, 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>(); Log.Verbose("Evaluate completions for {XmlLocation:l}", location); using (await projectDocument.Lock.ReaderLockAsync()) { if (!projectDocument.EnableExpressions) { return(null); } ExpressionNode expression; Range expressionRange; if (!location.IsExpression(out expression, out expressionRange)) { Log.Verbose("Not offering any completions for {XmlLocation:l} (not on an expression or a location where an expression can be added).", location); return(null); } if (expression.Kind != ExpressionKind.Evaluate) { Log.Verbose("Not offering any completions for {XmlLocation:l} (this provider only supports MSBuild Evaluation expressions, not {ExpressionKind} expressions).", location, expression.Kind); return(null); } Log.Verbose("Offering completions to replace Evaluate expression @ {ReplaceRange:l}", expressionRange ); completions.AddRange( GetCompletionItems(projectDocument, expressionRange) ); } Log.Verbose("Offering {CompletionCount} completion(s) for {XmlLocation:l}", completions.Count, location); if (completions.Count == 0) { return(null); } return(new CompletionList(completions, isIncomplete: false // Consider this list to be exhaustive )); }
public void IsExpression_Success(string testFileName, int line, int column, ExpressionKind expectedExpressionKind) { 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 location = locator.Inspect(testPosition); Assert.NotNull(location); ExpressionNode actualExpression; Range actualExpressionRange; Assert.True( location.IsExpression(out actualExpression, out actualExpressionRange), "IsExpression" ); Assert.NotNull(actualExpression); Assert.Equal(expectedExpressionKind, actualExpression.Kind); }
/// <summary> /// Provide completions for the specified location. /// </summary> /// <param name="location"> /// The <see cref="XmlLocation"/> where completions are requested. /// </param> /// <param name="triggerCharacters"> /// The character(s), if any, that triggered completion. /// </param> /// <param name="projectDocument"> /// The <see cref="ProjectDocument"/> that contains the <paramref name="location"/>. /// </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>(); Log.Verbose("Evaluate completions for {XmlLocation:l}", location); using (await projectDocument.Lock.ReaderLockAsync()) { if (!projectDocument.EnableExpressions) { return(null); } ExpressionNode expression; Range expressionRange; if (!location.IsExpression(out expression, out expressionRange)) { Log.Verbose("Not offering any completions for {XmlLocation:l} (not on an expression or a location where an expression can be added).", location); return(null); } // AF: This code is getting complicated; refactor, please. if (expression is Symbol) { expression = expression.Parent; // The containing expression. } // These are handled by ItemGroupExpressionCompletion. if (expression is ItemGroup) { Log.Verbose("Not offering any completions for {XmlLocation:l} (location represents an item group expression, not an item metadata expression).", location); return(null); } bool offerItemTypes = true; // By default, we offer item types. string targetItemType = null; if (expression is ItemMetadata metadataExpression && metadataExpression.HasItemType) { targetItemType = metadataExpression.ItemType; offerItemTypes = false; // We don't offer item types if one is already specified in the metadata expression. } bool offerUnqualifiedCompletions = false; if (location.IsAttribute(out XSAttribute attribute) && attribute.Element.ParentElement?.Name == "ItemGroup") { // An attribute on an item element. targetItemType = targetItemType ?? attribute.Element.Name; offerUnqualifiedCompletions = true; }