/// <summary> /// The method performs a preliminarily filtering of commit availability. /// In case of a doubt, it should respond with true. /// We will be able to cancel later in /// <see cref="TryCommit(IAsyncCompletionSession, ITextBuffer, VSCompletionItem, char, CancellationToken)"/> /// based on <see cref="VSCompletionItem"/> item, e.g. based on <see cref="CompletionItemRules"/>. /// </summary> public bool ShouldCommitCompletion( IAsyncCompletionSession session, SnapshotPoint location, char typedChar, CancellationToken cancellationToken) { if (!PotentialCommitCharacters.Contains(typedChar)) { return(false); } return(!(session.Properties.TryGetProperty(CompletionSource.ExcludedCommitCharacters, out ImmutableArray <char> excludedCommitCharacter) && excludedCommitCharacter.Contains(typedChar))); }
public async Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) { var items = ImmutableArray.CreateBuilder <CompletionItem>(); items.AddRange(keywords); foreach (var item in this.document.GetCompletions(triggerLocation.Position)) { items.Add(new CompletionItem(item, this, CompletionItemIcon)); } var result = new CompletionContext(items.ToImmutable()); return(await Task.FromResult(result)); }
Task <object> ICompletionDocumentationProvider.GetDocumentationAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) { if (!item.Properties.TryGetProperty <BaseInfo> (typeof(BaseInfo), out var info) || info == null) { return(Task.FromResult <object> (null)); } if (!session.Properties.TryGetProperty <MSBuildCompletionSessionContext> (typeof(MSBuildCompletionSessionContext), out var context)) { return(Task.FromResult <object> (null)); } return(DisplayElementFactory.GetInfoTooltipElement(context.doc, info, context.rr, token)); }
async Task <List <CompletionItem> > GetPackageNameCompletions(IAsyncCompletionSession session, MSBuildRootDocument doc, string searchQuery, CancellationToken token) { var tfm = doc.GetTargetFrameworkNuGetSearchParameter(); session.Properties.AddProperty(typeof(NuGetSearchUpdater), new NuGetSearchUpdater(this, session, tfm)); if (string.IsNullOrEmpty(searchQuery)) { return(null); } var results = await provider.PackageSearchManager.SearchPackageNames(searchQuery.ToLower(), tfm).ToTask(token); return(CreateNuGetItemsFromSearchResults(results)); }
public async Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) { var document = applicableToSpan.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); Microsoft.CodeAnalysis.Completion.CompletionTrigger CreateTrigger() { switch (trigger.Reason) { case CompletionTriggerReason.Invoke: case CompletionTriggerReason.InvokeAndCommitIfUnique: return(Microsoft.CodeAnalysis.Completion.CompletionTrigger.Invoke); case CompletionTriggerReason.Insertion: return(Microsoft.CodeAnalysis.Completion.CompletionTrigger.CreateInsertionTrigger(trigger.Character)); case CompletionTriggerReason.Backspace: case CompletionTriggerReason.Deletion: return(Microsoft.CodeAnalysis.Completion.CompletionTrigger.CreateDeletionTrigger(trigger.Character)); default: throw new ArgumentOutOfRangeException(); } } var completions = await _completionService.GetCompletionsAsync( document, triggerLocation.Position, CreateTrigger(), document.Workspace.Options, cancellationToken : token); if (completions == null) { return(CompletionContext.Empty); } var items = completions.Items .Select(x => { var item = new CompletionItem(x.DisplayText, this, new ImageElement(x.Glyph.GetImageId())); item.Properties.AddProperty("Document", document); item.Properties.AddProperty("CompletionItem", x); return(item); }) .ToImmutableArray(); return(new CompletionContext(items)); }
public Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) { if (item is null) { throw new ArgumentNullException(nameof(item)); } if (!item.Properties.TryGetProperty(DescriptionKey, out AggregateBoundAttributeDescription completionDescription)) { return(Task.FromResult <object>(string.Empty)); } var description = _descriptionFactory.CreateClassifiedDescription(completionDescription); return(Task.FromResult <object>(description)); }
public bool ShouldCommitCompletion(IAsyncCompletionSession session, SnapshotPoint location, char typedChar, CancellationToken token) { if (Array.IndexOf(allCommitChars, typedChar) < 0) { return(false); } // only handle sessions that XML completion participated in // although we aren't told what exact item we might be committing yet, the trigger tells us enough // about the kind of item to allow us to specialize the commit chars if (!session.Properties.TryGetProperty(typeof(XmlCompletionTrigger), out XmlCompletionTrigger kind)) { return(false); } ; switch (kind) { case XmlCompletionTrigger.Element: case XmlCompletionTrigger.ElementWithBracket: // allow using / as a commit char for elements as self-closing elements, but special case disallowing it // in the cases where that could conflict with typing the / at the start of a closing tag if (typedChar == '/') { var span = session.ApplicableToSpan.GetSpan(location.Snapshot); if (span.Length == (kind == XmlCompletionTrigger.Element ? 0 : 1)) { return(false); } } return(Array.IndexOf(tagCommitChars, typedChar) > -1); case XmlCompletionTrigger.Attribute: return(Array.IndexOf(attributeCommitChars, typedChar) > -1); case XmlCompletionTrigger.AttributeValue: return(Array.IndexOf(attributeValueCommitChars, typedChar) > -1); case XmlCompletionTrigger.DocType: return(Array.IndexOf(tagCommitChars, typedChar) > -1); case XmlCompletionTrigger.Entity: return(Array.IndexOf(entityCommitChars, typedChar) > -1); } return(false); }
public async Task <CompletionContext> GetCompletionContextAsync( IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableSpan, CancellationToken token) { _foregroundDispatcher.AssertBackgroundThread(); try { var syntaxTree = await _parser.GetLatestSyntaxTreeAsync(triggerLocation.Snapshot, token); var location = new SourceSpan(triggerLocation.Position, 0); var razorCompletionItems = _completionFactsService.GetCompletionItems(syntaxTree, location); var completionItems = new List <CompletionItem>(); foreach (var razorCompletionItem in razorCompletionItems) { if (razorCompletionItem.Kind != RazorCompletionItemKind.Directive) { // Don't support any other types of completion kinds other than directives. continue; } var completionItem = new CompletionItem( displayText: razorCompletionItem.DisplayText, filterText: razorCompletionItem.DisplayText, insertText: razorCompletionItem.InsertText, source: this, icon: DirectiveImageGlyph, filters: DirectiveCompletionFilters, suffix: string.Empty, sortText: razorCompletionItem.DisplayText, attributeIcons: ImmutableArray <ImageElement> .Empty); completionItem.Properties.AddProperty(DescriptionKey, razorCompletionItem.Description); completionItems.Add(completionItem); } var context = new CompletionContext(completionItems.ToImmutableArray()); return(context); } catch (OperationCanceledException) { return(CompletionContext.Empty); } }
internal Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) { CompletionContext context; if (TextView.TryGetWordCompletionData(out var wordCompletionData)) { var itemsRaw = wordCompletionData.WordCollection.Select(x => new CompletionItem(x, this)).ToArray(); var items = ImmutableArray.Create <CompletionItem>(itemsRaw); context = new CompletionContext(items); } else { context = CompletionContext.Empty; } return(Task.FromResult(context)); }
public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) { if (item.Properties.TryGetProperty(ResolvePropertyKey, out Func <Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem, Task <Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem> > resolver) && resolver != null && item.Properties.TryGetProperty(ProtocolItemKey, out Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem unresolvedItem)) { var resolvedItem = await resolver(unresolvedItem); if (resolvedItem != null) { unresolvedItem.TextEdit = unresolvedItem.TextEdit ?? resolvedItem.TextEdit; unresolvedItem.AdditionalTextEdits = unresolvedItem.AdditionalTextEdits ?? resolvedItem.AdditionalTextEdits; return(resolvedItem?.Detail); } } return(null); }
public CommitResult TryCommit(IAsyncCompletionSession session, ITextBuffer buffer, CompletionItem item, char typedChar, CancellationToken token) { if (!item.TryGetKind(out var kind)) { return(CommitResult.Unhandled); } switch (kind) { case XmlCompletionItemKind.MultipleClosingTags: case XmlCompletionItemKind.ClosingTag: InsertClosingTags(session, buffer, item); return(CommitResult.Handled); } LoggingService.LogWarning($"XML commit manager did not handle unknown special completion kind {kind}"); return(CommitResult.Unhandled); }
public Task <ImmutableArray <VSCompletionItem> > SortCompletionListAsync( IAsyncCompletionSession session, AsyncCompletionSessionInitialDataSnapshot data, CancellationToken cancellationToken) { if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isExperimentEnabled) && isExperimentEnabled) { AsyncCompletionLogger.LogSessionHasExperimentEnabled(); // This method is called exactly once, so use the opportunity to set a baseline for telemetry. if (data.InitialList.Any(i => i.Filters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))) { AsyncCompletionLogger.LogSessionContainsTargetTypeFilter(); } } return(Task.FromResult(data.InitialList.OrderBy(i => i.SortText).ToImmutableArray())); }
protected override Task <CompletionContext> GetAttributeCompletionsAsync( IAsyncCompletionSession session, SnapshotPoint triggerLocation, List <XObject> nodePath, IAttributedXObject attributedObject, Dictionary <string, string> existingAtts, CancellationToken token) { if (nodePath.LastOrDefault() is XElement xel && xel.NameEquals("Hello", true)) { var item = new CompletionItem("There", this) .AddKind(XmlCompletionItemKind.Attribute); var items = ImmutableArray <CompletionItem> .Empty; items = items.Add(item); return(Task.FromResult(new CompletionContext(items))); } return(Task.FromResult(CompletionContext.Empty)); }
public CompletionContinuation HandleTypedChar(IAsyncCompletionSession session, VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem selectedItem, SnapshotPoint location, char typedChar, CancellationToken token) { if (this.serverCommitCharacters.Contains(typedChar)) { return(CompletionContinuation.Commit); } if (selectedItem?.CommitCharacters.Contains(typedChar) == true) { return(CompletionContinuation.Commit); } if (this.ShouldContinue(typedChar)) { return(CompletionContinuation.Continue); } return(CompletionContinuation.Dismiss); }
public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) { if (item.Properties.TryGetProperty("knownmoniker", out string name)) { try { PropertyInfo property = typeof(KnownMonikers).GetProperty(name, BindingFlags.Static | BindingFlags.Public); var moniker = (ImageMoniker)property.GetValue(null, null); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var image = new CrispImage { Source = await moniker.ToBitmapSourceAsync(100), Height = 100, Width = 100, }; return(image); } catch (Exception ex) { System.Diagnostics.Debug.Write(ex); } } else if (item.Properties.TryGetProperty("IsGlobal", out bool isGlobal) && isGlobal) { var img = GetFileName(item.DisplayText); if (!string.IsNullOrEmpty(img)) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); return(new Image { Source = new BitmapImage(new Uri(img)), MaxHeight = 720 // VS minimum requirements is 1280x720 }); } } return(null); }
// this is primarily used to pass info from GetCompletionContextAsync to GetDocumentationAsync // but also reuses the values calculated for expression completion in GetCompletionContextAsync // if it's determined not be be expression completion but actually ends up // in GetElementCompletionsAsync or GetAttributeCompletionsAsync async Task <MSBuildCompletionSessionContext> GetSessionContext(IAsyncCompletionSession session, SnapshotPoint triggerLocation, CancellationToken token) { if (session.Properties.TryGetProperty <MSBuildCompletionSessionContext> (typeof(MSBuildCompletionSessionContext), out var context)) { return(context); } var parser = GetParser(); var parseResult = await parser.GetOrParseAsync((ITextSnapshot2)triggerLocation.Snapshot, token); var doc = parseResult.MSBuildDocument ?? MSBuildRootDocument.Empty; var spine = parser.GetSpineParser(triggerLocation); var rr = MSBuildResolver.Resolve(GetSpineParser(triggerLocation), triggerLocation.Snapshot.GetTextSource(), doc, provider.FunctionTypeProvider); context = new MSBuildCompletionSessionContext { doc = doc, rr = rr, spine = spine }; session.Properties.AddProperty(typeof(MSBuildCompletionSessionContext), context); return(context); }
// this is primarily used to pass info from GetCompletionContextAsync to GetDocumentationAsync // but also reuses the values calculated for expression completion in GetCompletionContextAsync // if it's determined not be be expression completion but actually ends up // in GetElementCompletionsAsync or GetAttributeCompletionsAsync async Task <MSBuildCompletionSessionContext> GetSessionContext(IAsyncCompletionSession session, SnapshotPoint triggerLocation, CancellationToken token) { if (session.Properties.TryGetProperty <MSBuildCompletionSessionContext> (typeof(MSBuildCompletionSessionContext), out var context)) { return(context); } MSBuildParseResult parseResult = parser.LastOutput ?? await parser.GetOrProcessAsync(triggerLocation.Snapshot, token); var doc = parseResult.MSBuildDocument ?? MSBuildRootDocument.Empty; var spine = XmlParser.GetSpineParser(triggerLocation); // clone the spine because the resolver alters it var rr = MSBuildResolver.Resolve(spine.Clone(), triggerLocation.Snapshot.GetTextSource(), doc, provider.FunctionTypeProvider, token); context = new MSBuildCompletionSessionContext { doc = doc, rr = rr, spine = spine }; session.Properties.AddProperty(typeof(MSBuildCompletionSessionContext), context); return(context); }
public async Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) { var text = triggerLocation.Snapshot.GetText(0, triggerLocation.Position); var data = await DebuggingService.GetCompletionDataAsync(DebuggingService.CurrentFrame, text, token); if (data == null) { return(new CompletionContext(ImmutableArray <CompletionItem> .Empty)); } var builder = ImmutableArray.CreateBuilder <CompletionItem> (data.Items.Count); foreach (var item in data.Items) { var image = new ImageElement(ObjectValueTreeViewController.GetImageId(item.Flags)); builder.Add(new CompletionItem(item.Name, this, image)); } return(new CompletionContext(builder.MoveToImmutable())); }
public async Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken cancellationToken) { var completionContexts = await ComputeNonEmptyCompletionContextsAsync(triggerLocation, applicableToSpan, cancellationToken); if (completionContexts.Length == 0) { return(null); } var completionItems = new LinkedList <CompletionItem>(); foreach (var context in completionContexts) { foreach (var item in context.Items) { var completionItem = item.CreateVsCompletionItem(this); completionItems.AddLast(completionItem); } } return(new CompletionContext(completionItems.ToImmutableArray())); }
public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, VSCompletionItem item, CancellationToken cancellationToken) { if (!item.Properties.TryGetProperty(RoslynItem, out RoslynCompletionItem roslynItem) || !session.Properties.TryGetProperty(TriggerSnapshot, out ITextSnapshot triggerSnapshot)) { return(null); } var document = triggerSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return(null); } var service = document.GetLanguageService <CompletionService>(); if (service == null) { return(null); } var description = await service.GetDescriptionAsync(document, roslynItem, cancellationToken).ConfigureAwait(false); var elements = IntelliSense.Helpers.BuildClassifiedTextElements(description.TaggedParts).ToArray(); if (elements.Length == 0) { return(new ClassifiedTextElement()); } else if (elements.Length == 1) { return(elements[0]); } else { return(new ContainerElement(ContainerElementStyle.Stacked | ContainerElementStyle.VerticalPadding, elements)); } }
public async Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) { // See whether we are in the key or value portion of the pair var lineStart = triggerLocation.GetContainingLine().Start; var spanBeforeCaret = new SnapshotSpan(lineStart, triggerLocation); var textBeforeCaret = triggerLocation.Snapshot.GetText(spanBeforeCaret); var colonIndex = textBeforeCaret.IndexOf(':'); var colonExistsBeforeCaret = colonIndex != -1; // User is likely in the key portion of the pair if (!colonExistsBeforeCaret) { return(GetContextForKey()); } // User is likely in the value portion of the pair. Try to provide extra items based on the key. var KeyExtractingRegex = new Regex(@"\W*(\w+)\W*:"); var key = KeyExtractingRegex.Match(textBeforeCaret); var candidateName = key.Success ? key.Groups.Count > 0 && key.Groups[1].Success ? key.Groups[1].Value : string.Empty : string.Empty; return(GetContextForValue(candidateName)); }
public override async Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) { var context = await GetSessionContext(session, triggerLocation, token); var rr = context.rr; var doc = context.doc; var spine = context.spine; if (rr?.ElementSyntax != null) { var reason = ConvertReason(trigger.Reason, trigger.Character); if (reason.HasValue && IsPossibleExpressionCompletionContext(spine)) { string expression = spine.GetIncompleteValue(triggerLocation.Snapshot); var triggerState = GetTriggerState( expression, reason.Value, trigger.Character, rr.IsCondition(), out int triggerLength, out ExpressionNode triggerExpression, out var listKind, out IReadOnlyList <ExpressionNode> comparandVariables ); if (triggerState != TriggerState.None) { var info = rr.GetElementOrAttributeValueInfo(doc); if (info != null && info.ValueKind != MSBuildValueKind.Nothing) { session.Properties.AddProperty(typeof(TriggerState), triggerState); return(await GetExpressionCompletionsAsync( session, info, triggerState, listKind, triggerLength, triggerExpression, comparandVariables, rr, triggerLocation, doc, token)); } } } } return(await base.GetCompletionContextAsync(session, trigger, triggerLocation, applicableToSpan, token)); }
public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) { var content = new ContainerElement( ContainerElementStyle.Wrapped, CompletionItemIcon, new ClassifiedTextElement( new ClassifiedTextRun(PredefinedClassificationTypeNames.Keyword, "Hello!"), new ClassifiedTextRun(PredefinedClassificationTypeNames.Identifier, " This is a sample item"))); var lineInfo = new ClassifiedTextElement( new ClassifiedTextRun( PredefinedClassificationTypeNames.Comment, "You are on line " + ((int)(session.Properties["LineNumber"]) + 1).ToString())); var timeInfo = new ClassifiedTextElement( new ClassifiedTextRun( PredefinedClassificationTypeNames.Identifier, "and it is " + DateTime.Now.ToShortTimeString())); return(new ContainerElement( ContainerElementStyle.Stacked, content, lineInfo, timeInfo)); }
public Task <ImmutableArray <VSCompletionItem> > SortCompletionListAsync( IAsyncCompletionSession session, AsyncCompletionSessionInitialDataSnapshot data, CancellationToken cancellationToken) { var stopwatch = SharedStopwatch.StartNew(); var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // This method is called exactly once, so use the opportunity to set a baseline for telemetry. if (sessionData.TargetTypeFilterExperimentEnabled) { AsyncCompletionLogger.LogSessionHasTargetTypeFilterEnabled(); if (data.InitialList.Any(i => i.Filters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))) { AsyncCompletionLogger.LogSessionContainsTargetTypeFilter(); } } // Sort by default comparer of Roslyn CompletionItem var sortedItems = data.InitialList.OrderBy(CompletionItemData.GetOrAddDummyRoslynItem).ToImmutableArray(); AsyncCompletionLogger.LogItemManagerSortTicksDataPoint((int)stopwatch.Elapsed.TotalMilliseconds); return(Task.FromResult(sortedItems)); }
public CommitResult TryCommit(IAsyncCompletionSession session, ITextBuffer buffer, CompletionItem item, char typedChar, CancellationToken token) { if (!item.Properties.TryGetProperty <MSBuildSpecialCommitKind> (typeof(MSBuildSpecialCommitKind), out var kind)) { return(CommitResult.Unhandled); } switch (kind) { case MSBuildSpecialCommitKind.NewGuid: InsertGuid(session, buffer); return(CommitResult.Handled); case MSBuildSpecialCommitKind.ItemReference: case MSBuildSpecialCommitKind.PropertyReference: Insert(session, buffer, item.InsertText); RetriggerCompletion(session.TextView); //TODO: insert trailing ) return(CommitResult.Handled); } LoggingService.LogError($"MSBuild commit manager did not handle unknown special completion kind {kind}"); return(CommitResult.Unhandled); }
Task <object> ICompletionDocumentationProvider.GetDocumentationAsync( IAsyncCompletionSession session, CompletionItem item, CancellationToken token) { if (!session.Properties.TryGetProperty <MSBuildCompletionSessionContext> (typeof(MSBuildCompletionSessionContext), out var context)) { return(Task.FromResult <object> (null)); } // note that the value is a tuple despite the key if (item.Properties.TryGetProperty <Tuple <string, FeedKind> > (typeof(Tuple <string, FeedKind>), out var packageSearchResult)) { return(GetPackageDocumentationAsync(context.doc, packageSearchResult.Item1, packageSearchResult.Item2, token)); } if (item.Properties.TryGetProperty <BaseInfo> (typeof(BaseInfo), out var info) && info != null) { return(provider.DisplayElementFactory.GetInfoTooltipElement( session.TextView.TextBuffer, context.doc, info, context.rr, token )); } return(Task.FromResult <object> (null)); }
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); }
private FilteredCompletionModel UpdateCompletionList( IAsyncCompletionSession session, AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { if (!session.Properties.TryGetProperty(CompletionSource.HasSuggestionItemOptions, out bool hasSuggestedItemOptions)) { // This is the scenario when the session is created out of Roslyn, in some other provider, e.g. in Debugger. // For now, the default hasSuggestedItemOptions is false. hasSuggestedItemOptions = false; } hasSuggestedItemOptions |= data.DisplaySuggestionItem; var filterText = session.ApplicableToSpan.GetText(data.Snapshot); var reason = data.Trigger.Reason; if (!session.Properties.TryGetProperty(CompletionSource.InitialTriggerKind, out CompletionTriggerKind initialRoslynTriggerKind)) { initialRoslynTriggerKind = CompletionTriggerKind.Invoke; } // Check if the user is typing a number. If so, only proceed if it's a number // directly after a <dot>. That's because it is actually reasonable for completion // to be brought up after a <dot> and for the user to want to filter completion // items based on a number that exists in the name of the item. However, when // we are not after a dot (i.e. we're being brought up after <space> is typed) // then we don't want to filter things. Consider the user writing: // // dim i =<space> // // We'll bring up the completion list here (as VB has completion on <space>). // If the user then types '3', we don't want to match against Int32. if (filterText.Length > 0 && char.IsNumber(filterText[0])) { if (!IsAfterDot(data.Snapshot, session.ApplicableToSpan)) { // Dismiss the session. return(null); } } // We need to filter if a non-empty strict subset of filters are selected var selectedFilters = data.SelectedFilters.Where(f => f.IsSelected).Select(f => f.Filter).ToImmutableArray(); var needToFilter = selectedFilters.Length > 0 && selectedFilters.Length < data.SelectedFilters.Length; var filterReason = Helpers.GetFilterReason(data.Trigger); // If the session was created/maintained out of Roslyn, e.g. in debugger; no properties are set and we should use data.Snapshot. // However, we prefer using the original snapshot in some projection scenarios. if (!session.Properties.TryGetProperty(CompletionSource.TriggerSnapshot, out ITextSnapshot snapshotForDocument)) { snapshotForDocument = data.Snapshot; } var document = snapshotForDocument.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); var completionService = document?.GetLanguageService <CompletionService>(); var completionRules = completionService?.GetRules() ?? CompletionRules.Default; var completionHelper = document != null?CompletionHelper.GetHelper(document) : _defaultCompletionHelper; var initialListOfItemsToBeIncluded = new List <ExtendedFilterResult>(); foreach (var item in data.InitialSortedList) { cancellationToken.ThrowIfCancellationRequested(); if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedFilters)) { continue; } if (!item.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem)) { roslynItem = RoslynCompletionItem.Create( displayText: item.DisplayText, filterText: item.FilterText, sortText: item.SortText, displayTextSuffix: item.Suffix); } if (MatchesFilterText(completionHelper, roslynItem, filterText, initialRoslynTriggerKind, filterReason, _recentItemsManager.RecentItems)) { initialListOfItemsToBeIncluded.Add(new ExtendedFilterResult(item, new FilterResult(roslynItem, filterText, matchedFilterText: true))); } else { // The item didn't match the filter text. We'll still keep it in the list // if one of two things is true: // // 1. The user has only typed a single character. In this case they might // have just typed the character to get completion. Filtering out items // here is not desirable. // // 2. They brough up completion with ctrl-j or through deletion. In these // cases we just always keep all the items in the list. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion || initialRoslynTriggerKind == CompletionTriggerKind.Invoke || filterText.Length <= 1) { initialListOfItemsToBeIncluded.Add(new ExtendedFilterResult(item, new FilterResult(roslynItem, filterText, matchedFilterText: false))); } } } if (data.Trigger.Reason == CompletionTriggerReason.Backspace && completionRules.DismissIfLastCharacterDeleted && session.ApplicableToSpan.GetText(data.Snapshot).Length == 0) { // Dismiss the session return(null); } if (initialListOfItemsToBeIncluded.Count == 0) { return(HandleAllItemsFilteredOut(reason, data.SelectedFilters, completionRules)); } var options = document?.Project.Solution.Options; var highlightMatchingPortions = options?.GetOption(CompletionOptions.HighlightMatchingPortionsOfCompletionListItems, document.Project.Language) ?? true; var showCompletionItemFilters = options?.GetOption(CompletionOptions.ShowCompletionItemFilters, document.Project.Language) ?? true; var updatedFilters = showCompletionItemFilters ? GetUpdatedFilters(initialListOfItemsToBeIncluded, data.SelectedFilters) : ImmutableArray <CompletionFilterWithState> .Empty; var highlightedList = GetHighlightedList(initialListOfItemsToBeIncluded, filterText, completionHelper, highlightMatchingPortions).ToImmutableArray(); // If this was deletion, then we control the entire behavior of deletion ourselves. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion) { return(HandleDeletionTrigger(data.Trigger.Reason, initialListOfItemsToBeIncluded, filterText, updatedFilters, highlightedList)); } Func <ImmutableArray <RoslynCompletionItem>, string, ImmutableArray <RoslynCompletionItem> > filterMethod; if (completionService == null) { filterMethod = (items, text) => CompletionService.FilterItems(completionHelper, items, text); } else { filterMethod = (items, text) => completionService.FilterItems(document, items, text); } return(HandleNormalFiltering( filterMethod, filterText, updatedFilters, initialRoslynTriggerKind, filterReason, data.Trigger.Character, initialListOfItemsToBeIncluded, highlightedList, completionHelper, hasSuggestedItemOptions)); }
public CommitResult TryCommit(IAsyncCompletionSession session, ITextBuffer buffer, CompletionItem item, char typedChar, CancellationToken token) { if (item.Properties.TryGetProperty(ReplAsyncCompletionSource.ProtocolItemKey, out Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem completionItem) && item.Properties.TryGetProperty(ReplAsyncCompletionSource.TriggerPointKey, out SnapshotPoint triggerLocation)) { // Tab and Enter characters should also commit. if (typedChar != '\t' && typedChar != '\n') { bool isCommitCharacter = false; if (completionItem.CommitCharacters != null && completionItem.CommitCharacters.Length != 0) { // If there are commit characters set for the particular completion item, then it should take precedence. foreach (var completionItemCommitCharacter in completionItem.CommitCharacters) { if (completionItemCommitCharacter.Length > 0 && completionItemCommitCharacter[0] == typedChar) { isCommitCharacter = true; break; } } if (!isCommitCharacter) { return(new CommitResult(isHandled: false, behavior: CommitBehavior.CancelCommit)); } } // TODO: Review if this should be removed if server passes in CommitTriggers for each CompletionItem. This is in place to unblock VC headless VS demo for build 2019 if (!isCommitCharacter && this.typicalDimissChars.Contains(typedChar)) { // If we got here it means the completion item has not specificed commit characters and we want to dismiss the commit and the intellisense session because it's a dismiss character. return(CommitResult.Handled); } } if (completionItem.TextEdit != null || completionItem.AdditionalTextEdits != null) { // Completion text edits are computed when the completion session is first triggered. The lines typed // after the completion session was started need to be deleted to revert the document to its original state. var caretPositionAtBuffer = session.TextView.GetCaretPointAtSubjectBuffer(buffer); if (caretPositionAtBuffer.HasValue) { var deleteTextLength = caretPositionAtBuffer.Value.Position - triggerLocation.Position; if (deleteTextLength > 0) { var deleteSpan = new Span(triggerLocation.Position, deleteTextLength); buffer.Delete(deleteSpan); } if (completionItem.TextEdit != null) { LspEditorUtilities.ApplyTextEdit(completionItem.TextEdit, triggerLocation.Snapshot, buffer); } else if (completionItem.InsertText != null) { buffer.Replace(session.ApplicableToSpan.GetSpan(buffer.CurrentSnapshot), completionItem.InsertText); } if (completionItem.AdditionalTextEdits != null) { LspEditorUtilities.ApplyTextEdits(completionItem.AdditionalTextEdits, triggerLocation.Snapshot, buffer); } return(CommitResult.Handled); } } } return(CommitResult.Unhandled); }
public bool ShouldCommitCompletion(IAsyncCompletionSession session, SnapshotPoint location, char typedChar, CancellationToken token) { return(true); }