private bool TryInvokeSnippetCompletion(TabKeyCommandArgs args, CompletionService completionService) { var subjectBuffer = args.SubjectBuffer; var caretPoint = args.TextView.GetCaretPoint(subjectBuffer).Value.Position; var text = subjectBuffer.AsTextContainer().CurrentText; // Delete the ? and invoke completion Workspace workspace = null; if (Workspace.TryGetWorkspace(subjectBuffer.AsTextContainer(), out workspace)) { var documentId = workspace.GetDocumentIdInCurrentContext(subjectBuffer.AsTextContainer()); if (documentId != null) { var document = workspace.CurrentSolution.GetDocument(documentId); if (document != null) { var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); if (caretPoint >= 2 && text[caretPoint - 1] == '?' && QuestionMarkIsPrecededByIdentifierAndWhitespace(text, caretPoint - 2, syntaxFacts)) { var textChange = new TextChange(TextSpan.FromBounds(caretPoint - 1, caretPoint), string.Empty); workspace.ApplyTextChanges(documentId, textChange, CancellationToken.None); this.StartNewModelComputation(completionService, new CompletionTrigger(CompletionTriggerKind.Snippets), filterItems: false); return(true); } } } } return(false); }
/// <summary> /// Update the solution so that the document with the Id has the text changes /// </summary> internal static void ApplyTextChanges( this Workspace workspace, DocumentId id, TextChange textChange, CancellationToken cancellationToken ) => workspace.ApplyTextChanges( id, SpecializedCollections.SingletonEnumerable(textChange), cancellationToken );
private bool TryInvokeSnippetCompletion(TabKeyCommandArgs args) { var subjectBuffer = args.SubjectBuffer; var caretPoint = args.TextView.GetCaretPoint(subjectBuffer).Value.Position; var text = subjectBuffer.AsTextContainer().CurrentText; // If the user types "<line start><spaces><question><tab>" // then the editor takes over and shows the normal *full* snippet picker UI. // i.e. the picker with all the folders and snippet organization. // // However, if the user instead has something like // // "<line start><spaces><identifier><question><tab>" // // Then we take over and we show a completion list with all snippets in it // in a flat list. This enables simple browsing and filtering of all items // based on what the user typed so far. // // If we detect this pattern, then we delete the previous character (the // question mark) and we don't send the tab through to the editor. In // essence, the <quesiton><tab> acts as the trigger, and we act as if that // text never makes it into the buffer. Workspace workspace = null; if (!Workspace.TryGetWorkspace(subjectBuffer.AsTextContainer(), out workspace)) { return(false); } var documentId = workspace.GetDocumentIdInCurrentContext(subjectBuffer.AsTextContainer()); if (documentId == null) { return(false); } var document = workspace.CurrentSolution.GetDocument(documentId); if (document == null) { return(false); } // There's was a buffer-Document mapping. We should be able // to get a CompletionService. var completionService = GetCompletionService(); Contract.ThrowIfNull(completionService, nameof(completionService)); var rules = completionService.GetRules(); if (rules.SnippetsRule != SnippetsRule.IncludeAfterTypingIdentifierQuestionTab) { return(false); } var syntaxFactsOpt = document.GetLanguageService <ISyntaxFactsService>(); if (syntaxFactsOpt == null || caretPoint < 2 || text[caretPoint - 1] != '?' || !QuestionMarkIsPrecededByIdentifierAndWhitespace(text, caretPoint - 1, syntaxFactsOpt)) { return(false); } // Because <question><tab> is actually a command to bring up snippets, // we delete the last <question> that was typed. var textChange = new TextChange(TextSpan.FromBounds(caretPoint - 1, caretPoint), string.Empty); workspace.ApplyTextChanges(documentId, textChange, CancellationToken.None); this.StartNewModelComputation( completionService, new CompletionTrigger(CompletionTriggerKind.Snippets), filterItems: false, dismissIfEmptyAllowed: true); return(true); }