/// <summary> /// Attempts to convert VS Completion trigger into Roslyn completion trigger /// </summary> /// <param name="trigger">VS completion trigger</param> /// <param name="triggerLocation">Character. /// VS provides Backspace and Delete characters inside the trigger while Roslyn needs the char deleted by the trigger. /// Therefore, we provide this character separately and use it for Delete and Backspace cases only. /// We retrieve this character from triggerLocation. /// </param> /// <returns>Roslyn completion trigger</returns> internal static RoslynTrigger GetRoslynTrigger(AsyncCompletionData.CompletionTrigger trigger, SnapshotPoint triggerLocation) { switch (trigger.Reason) { case AsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique: return(new RoslynTrigger(CompletionTriggerKind.InvokeAndCommitIfUnique)); case AsyncCompletionData.CompletionTriggerReason.Insertion: return(RoslynTrigger.CreateInsertionTrigger(trigger.Character)); case AsyncCompletionData.CompletionTriggerReason.Deletion: case AsyncCompletionData.CompletionTriggerReason.Backspace: var snapshotBeforeEdit = trigger.ViewSnapshotBeforeTrigger; char characterRemoved; if (triggerLocation.Position >= 0 && triggerLocation.Position < snapshotBeforeEdit.Length) { // If multiple characters were removed (selection), this finds the first character from the left. characterRemoved = snapshotBeforeEdit[triggerLocation.Position]; } else { characterRemoved = (char)0; } return(RoslynTrigger.CreateDeletionTrigger(characterRemoved)); case AsyncCompletionData.CompletionTriggerReason.SnippetsMode: return(new RoslynTrigger(CompletionTriggerKind.Snippets)); default: return(RoslynTrigger.Invoke); } }
protected async Task CheckResultsAsync( Document document, int position, string expectedItemOrNull, string expectedDescriptionOrNull, bool usePreviousCharAsTrigger, bool checkForAbsence, int?glyph, int?matchPriority, bool?hasSuggestionModeItem, string displayTextSuffix) { var code = (await document.GetTextAsync()).ToString(); var trigger = CompletionTrigger.Invoke; if (usePreviousCharAsTrigger) { trigger = CompletionTrigger.CreateInsertionTrigger(insertedCharacter: code.ElementAt(position - 1)); } var completionService = GetCompletionService(document.Project.Solution.Workspace); var completionList = await GetCompletionListAsync(completionService, document, position, trigger); var items = completionList == null ? ImmutableArray <CompletionItem> .Empty : completionList.Items; if (hasSuggestionModeItem != null) { Assert.Equal(hasSuggestionModeItem.Value, completionList.SuggestionModeItem != null); } if (checkForAbsence) { if (items == null) { return; } if (expectedItemOrNull == null) { Assert.Empty(items); } else { AssertEx.None( items, c => CompareItems(c.DisplayText, expectedItemOrNull) && (expectedDescriptionOrNull != null ? completionService.GetDescriptionAsync(document, c).Result.Text == expectedDescriptionOrNull : true)); } } else { if (expectedItemOrNull == null) { Assert.NotEmpty(items); } else { AssertEx.Any(items, c => CompareItems(c.DisplayText, expectedItemOrNull) && CompareItems(c.DisplayTextSuffix, displayTextSuffix ?? "") && (expectedDescriptionOrNull != null ? completionService.GetDescriptionAsync(document, c).Result.Text == expectedDescriptionOrNull : true) && (glyph.HasValue ? c.Tags.SequenceEqual(GlyphTags.GetTags((Glyph)glyph.Value)) : true) && (matchPriority.HasValue ? (int)c.Rules.MatchPriority == matchPriority.Value : true)); } } }
private void VerifyTextualTriggerCharacterWorker( string markup, bool expectedTriggerCharacter, bool triggerOnLetter) { using (var workspace = CreateWorkspace(markup)) { var document = workspace.Documents.Single(); var position = document.CursorPosition.Value; var text = document.TextBuffer.CurrentSnapshot.AsText(); var options = workspace.Options.WithChangedOption( CompletionOptions.TriggerOnTypingLetters, document.Project.Language, triggerOnLetter); var trigger = CompletionTrigger.CreateInsertionTrigger(text[position]); var service = GetCompletionService(workspace); var isTextualTriggerCharacterResult = service.ShouldTriggerCompletion(text, position + 1, trigger, options: options); if (expectedTriggerCharacter) { var assertText = "'" + text.ToString(new TextSpan(position, 1)) + "' expected to be textual trigger character"; Assert.True(isTextualTriggerCharacterResult, assertText); } else { var assertText = "'" + text.ToString(new TextSpan(position, 1)) + "' expected to NOT be textual trigger character"; Assert.False(isTextualTriggerCharacterResult, assertText); } } }
protected async Task <ImmutableArray <CompletionItem> > GetCompletionItemsAsync( string markup, SourceCodeKind sourceCodeKind, bool usePreviousCharAsTrigger = false) { MarkupTestFile.GetPosition(markup.NormalizeLineEndings(), out var code, out int position); var document = WorkspaceFixture.UpdateDocument(code, sourceCodeKind); var trigger = usePreviousCharAsTrigger ? CompletionTrigger.CreateInsertionTrigger(insertedCharacter: code.ElementAt(position - 1)) : CompletionTrigger.Invoke; var completionService = GetCompletionService(document.Project.Solution.Workspace); var completionList = await GetCompletionListAsync(completionService, document, position, trigger); return(completionList == null ? ImmutableArray <CompletionItem> .Empty : completionList.Items); }