async Task <CompletionContext> GetExpressionCompletionsAsync( IAsyncCompletionSession session, ValueInfo info, TriggerState triggerState, ListKind listKind, int triggerLength, ExpressionNode triggerExpression, IReadOnlyList <ExpressionNode> comparandVariables, MSBuildResolveResult rr, SnapshotPoint triggerLocation, MSBuildRootDocument doc, CancellationToken token) { var kind = info.InferValueKindIfUnknown(); if (!ValidateListPermitted(listKind, kind)) { return(CompletionContext.Empty); } bool allowExpressions = kind.AllowExpressions(); kind = kind.GetScalarType(); if (kind == MSBuildValueKind.Data || kind == MSBuildValueKind.Nothing) { return(CompletionContext.Empty); } bool isValue = triggerState == TriggerState.Value; var items = new List <CompletionItem> (); if (comparandVariables != null && isValue) { foreach (var ci in ExpressionCompletion.GetComparandCompletions(doc, comparandVariables)) { items.Add(CreateCompletionItem(ci, XmlCompletionItemKind.AttributeValue)); } } if (isValue) { switch (kind) { case MSBuildValueKind.NuGetID: if (triggerExpression is ExpressionText t) { var packageNameItems = await GetPackageNameCompletions(session, doc, t.Value, token); if (packageNameItems != null) { items.AddRange(packageNameItems); } } break; case MSBuildValueKind.NuGetVersion: { var packageVersionItems = await GetPackageVersionCompletions(doc, rr, token); if (packageVersionItems != null) { items.AddRange(packageVersionItems); } break; } case MSBuildValueKind.Sdk: case MSBuildValueKind.SdkWithVersion: { var sdkItems = await GetSdkCompletions(doc, token); if (sdkItems != null) { items.AddRange(sdkItems); } break; } case MSBuildValueKind.Guid: items.Add(CreateSpecialItem("New GUID", "Inserts a new GUID", KnownImages.Add, MSBuildSpecialCommitKind.NewGuid)); break; case MSBuildValueKind.Lcid: items.AddRange(GetLcidCompletions()); break; } } //TODO: better metadata support IEnumerable <BaseInfo> cinfos; if (info.CustomType != null && info.CustomType.Values.Count > 0 && isValue) { cinfos = info.CustomType.Values; } else { //FIXME: can we avoid awaiting this unless we actually need to resolve a function? need to propagate async downwards await provider.FunctionTypeProvider.EnsureInitialized(token); cinfos = ExpressionCompletion.GetCompletionInfos(rr, triggerState, kind, triggerExpression, triggerLength, doc, provider.FunctionTypeProvider); } if (cinfos != null) { foreach (var ci in cinfos) { items.Add(CreateCompletionItem(ci, XmlCompletionItemKind.AttributeValue)); } } if ((allowExpressions && isValue) || triggerState == TriggerState.BareFunctionArgumentValue) { items.Add(CreateSpecialItem("$(", "Property reference", KnownImages.MSBuildProperty, MSBuildSpecialCommitKind.PropertyReference)); } if (allowExpressions && isValue) { items.Add(CreateSpecialItem("@(", "Item reference", KnownImages.MSBuildItem, MSBuildSpecialCommitKind.ItemReference)); if (IsMetadataAllowed(triggerExpression, rr)) { items.Add(CreateSpecialItem("%(", "Metadata reference", KnownImages.MSBuildItem, MSBuildSpecialCommitKind.MetadataReference)); } } if (items.Count > 0) { return(CreateCompletionContext(items)); } return(CompletionContext.Empty); }