private Model( DisconnectedBufferGraph disconnectedBufferGraph, IList<CompletionItem> totalItems, IList<CompletionItem> filteredItems, CompletionItem selectedItem, bool isHardSelection, bool isUnique, bool useSuggestionCompletionMode, CompletionItem builder, CompletionItem defaultBuilder, CompletionTriggerInfo triggerInfo, ITrackingPoint commitSpanEndPoint, bool dismissIfEmpty) { Contract.ThrowIfNull(selectedItem); Contract.ThrowIfFalse(totalItems.Count != 0, "Must have at least one item."); Contract.ThrowIfFalse(filteredItems.Count != 0, "Must have at least one filtered item."); Contract.ThrowIfFalse(filteredItems.Contains(selectedItem) || defaultBuilder == selectedItem, "Selected item must be in filtered items."); _disconnectedBufferGraph = disconnectedBufferGraph; this.TotalItems = totalItems; this.FilteredItems = filteredItems; this.SelectedItem = selectedItem; this.IsHardSelection = isHardSelection; this.IsUnique = isUnique; this.UseSuggestionCompletionMode = useSuggestionCompletionMode; this.Builder = builder; this.DefaultBuilder = defaultBuilder; this.TriggerInfo = triggerInfo; this.CommitTrackingSpanEndPoint = commitSpanEndPoint; this.DismissIfEmpty = dismissIfEmpty; }
private Model( DisconnectedBufferGraph disconnectedBufferGraph, IList<CompletionItem> totalItems, IList<CompletionItem> filteredItems, CompletionItem selectedItem, ImmutableArray<CompletionItemFilter> completionItemFilters, ImmutableDictionary<CompletionItemFilter, bool> filterState, IReadOnlyDictionary<CompletionItem, string> completionItemToFilterText, bool isHardSelection, bool isUnique, bool useSuggestionCompletionMode, CompletionItem builder, CompletionItem defaultBuilder, CompletionTriggerInfo triggerInfo, ITrackingPoint commitSpanEndPoint, bool dismissIfEmpty) { Contract.ThrowIfFalse(totalItems.Count != 0, "Must have at least one item."); _disconnectedBufferGraph = disconnectedBufferGraph; this.TotalItems = totalItems; this.FilteredItems = filteredItems; this.FilterState = filterState; this.SelectedItem = selectedItem; this.CompletionItemFilters = completionItemFilters; this.CompletionItemToFilterText = completionItemToFilterText; this.IsHardSelection = isHardSelection; this.IsUnique = isUnique; this.UseSuggestionCompletionMode = useSuggestionCompletionMode; this.Builder = builder; this.DefaultBuilder = defaultBuilder; this.TriggerInfo = triggerInfo; this.CommitTrackingSpanEndPoint = commitSpanEndPoint; this.DismissIfEmpty = dismissIfEmpty; }
private PatternMatch? GetMatch( CompletionItem item, string filterText, bool includeMatchSpans, CultureInfo culture) { // If the item has a dot in it (i.e. for something like enum completion), then attempt // to match what the user wrote against the last portion of the name. That way if they // write "Bl" and we have "Blub" and "Color.Black", we'll consider hte latter to be a // better match as they'll both be prefix matches, and the latter will have a higher // priority. var lastDotIndex = item.FilterText.LastIndexOf('.'); if (lastDotIndex >= 0) { var textAfterLastDot = item.FilterText.Substring(lastDotIndex + 1); var match = GetMatchWorker(textAfterLastDot, filterText, includeMatchSpans, culture); if (match != null) { return match; } } // Didn't have a dot, or the user text didn't match the portion after the dot. // Just do a normal check against the entire completion item. return GetMatchWorker(item.FilterText, filterText, includeMatchSpans, culture); }
public override async Task<CompletionChange> GetChangeAsync(Document document, CompletionItem item, char? commitKey = default(char?), CancellationToken cancellationToken = default(CancellationToken)) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var newDocument = await DetermineNewDocumentAsync(item, text, cancellationToken).ConfigureAwait(false); var newText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); int? newPosition = null; // Attempt to find the inserted node and move the caret appropriately if (newRoot != null) { var caretTarget = newRoot.GetAnnotatedNodesAndTokens(_annotation).FirstOrNullable(); if (caretTarget != null) { var targetPosition = GetTargetCaretPosition(caretTarget.Value.AsNode()); // Something weird happened and we failed to get a valid position. // Bail on moving the caret. if (targetPosition > 0 && targetPosition <= newText.Length) { newPosition = targetPosition; } } } var changes = await newDocument.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false); return CompletionChange.Create(ImmutableArray.CreateRange(changes), newPosition, includesCommitCharacter: true); }
public override bool? IsCommitCharacter(CompletionItem completionItem, char ch, string textTypedSoFar) { // TODO(cyrusn): We could just allow the standard list of completion characters. // However, i'd like to see what the experience is like really filtering down to the set // of things that is allowable. return ch == ' ' || ch == '(' || ch == '{' || ch == '['; }
private async Task<Document> DetermineNewDocumentAsync(CompletionItem completionItem, SourceText sourceText, CancellationToken cancellationToken) { // The span we're going to replace var line = sourceText.Lines[MemberInsertionCompletionItem.GetLine(completionItem)]; //var line = textSnapshot.GetLineFromLineNumber(MemberInsertionCompletionItem.GetLine(completionItem)); //var sourceText = textSnapshot.AsText(); var document = sourceText.GetOpenDocumentInCurrentContextWithChanges(); Contract.ThrowIfNull(document); // Annotate the line we care about so we can find it after adding usings var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var token = GetToken(completionItem, tree, cancellationToken); var annotatedRoot = tree.GetRoot(cancellationToken).ReplaceToken(token, token.WithAdditionalAnnotations(_otherAnnotation)); document = document.WithSyntaxRoot(annotatedRoot); var memberContainingDocument = await GenerateMemberAndUsingsAsync(document, completionItem, line, cancellationToken).ConfigureAwait(false); var insertionRoot = await PrepareTreeForMemberInsertionAsync(memberContainingDocument, cancellationToken).ConfigureAwait(false); var insertionText = await GenerateInsertionTextAsync(memberContainingDocument, cancellationToken).ConfigureAwait(false); var destinationSpan = ComputeDestinationSpan(insertionRoot, insertionText); var finalText = insertionRoot.GetText(sourceText.Encoding).Replace(destinationSpan, insertionText.Trim()); document = document.WithText(finalText); var newRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var declaration = GetSyntax(newRoot.FindToken(destinationSpan.End)); document = document.WithSyntaxRoot(newRoot.ReplaceNode(declaration, declaration.WithAdditionalAnnotations(_annotation))); return Formatter.FormatAsync(document, _annotation, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); }
private CompletionList(TextSpan defaultSpan, ImmutableArray<CompletionItem> items, CompletionRules rules, CompletionItem suggestionModeItem) { this.DefaultSpan = defaultSpan; this.Items = items.IsDefault ? ImmutableArray<CompletionItem>.Empty : items; this.Rules = rules ?? CompletionRules.Default; this.SuggestionModeItem = suggestionModeItem; }
private bool IsCaretOutsideItemBounds( Model model, SnapshotPoint caretPoint, CompletionItem item, Dictionary<TextSpan, string> textSpanToText, Dictionary<TextSpan, ViewTextSpan> textSpanToViewSpan) { // Easy first check. See if the caret point is before the start of the item. if (!textSpanToViewSpan.TryGetValue(item.Span, out var filterSpanInViewBuffer)) { filterSpanInViewBuffer = model.GetViewBufferSpan(item.Span); textSpanToViewSpan[item.Span] = filterSpanInViewBuffer; } if (caretPoint < filterSpanInViewBuffer.TextSpan.Start) { return true; } var textSnapshot = caretPoint.Snapshot; var currentText = model.GetCurrentTextInSnapshot(item.Span, textSnapshot, textSpanToText); var currentTextSpan = new TextSpan(filterSpanInViewBuffer.TextSpan.Start, currentText.Length); return !currentTextSpan.IntersectsWith(caretPoint); }
protected virtual Task<CompletionDescription> GetDescriptionWorkerAsync( Document document, CompletionItem item, CancellationToken cancellationToken) { return CommonCompletionItem.HasDescription(item) ? Task.FromResult(CommonCompletionItem.GetDescription(item)) : Task.FromResult(CompletionDescription.Empty); }
private async Task<ImmutableArray<TaggedText>> TryAddSnippetInvocationPart( Document document, CompletionItem item, ImmutableArray<TaggedText> parts, CancellationToken cancellationToken) { var languageServices = document.Project.LanguageServices; var snippetService = languageServices.GetService<ISnippetInfoService>(); if (snippetService != null) { var change = await GetTextChangeAsync(document, item, ch: '\t', cancellationToken: cancellationToken).ConfigureAwait(false) ?? new TextChange(item.Span, item.DisplayText); var insertionText = change.NewText; if (snippetService != null && snippetService.SnippetShortcutExists_NonBlocking(insertionText)) { var note = string.Format(FeaturesResources.Note_colon_Tab_twice_to_insert_the_0_snippet, insertionText); if (parts.Any()) { parts = parts.Add(new TaggedText(TextTags.LineBreak, Environment.NewLine)); } parts = parts.Add(new TaggedText(TextTags.Text, note)); } } return parts; }
public void SetCompletionItems( IList<CompletionItem> completionItems, CompletionItem selectedItem, CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> completionItemFilters, string filterText) { this.AssertIsForeground(); // Initialize the completion map to a reasonable default initial size (+1 for the builder) CompletionItemMap = CompletionItemMap ?? new Dictionary<CompletionItem, VSCompletion>(completionItems.Count + 1); FilterText = filterText; SuggestionModeItem = suggestionModeItem; this.SetupFilters(completionItemFilters); CreateCompletionListBuilder(selectedItem, suggestionModeItem, suggestionMode); CreateNormalCompletionListItems(completionItems); var selectedCompletionItem = GetVSCompletion(selectedItem); VsCompletionSet.SelectionStatus = new CompletionSelectionStatus( selectedCompletionItem, isSelected: !isSoftSelected, isUnique: selectedCompletionItem != null); }
public override TextChange GetTextChange(CompletionItem selectedItem, char? ch = null, string textTypedSoFar = null) { var displayText = selectedItem.DisplayText; if (ch != null) { // If user types a space, do not complete the " =" (space and equals) at the end of a named parameter. The // typed space character will be passed through to the editor, and they can then type the '='. if (ch == ' ' && displayText.EndsWith(SpaceEqualsString, StringComparison.Ordinal)) { return new TextChange(selectedItem.FilterSpan, displayText.Remove(displayText.Length - SpaceEqualsString.Length)); } // If the user types '=', do not complete the '=' at the end of the named parameter because the typed '=' // will be passed through to the editor. if (ch == '=' && displayText.EndsWith(EqualsString, StringComparison.Ordinal)) { return new TextChange(selectedItem.FilterSpan, displayText.Remove(displayText.Length - EqualsString.Length)); } // If the user types ':', do not complete the ':' at the end of the named parameter because the typed ':' // will be passed through to the editor. if (ch == ':' && displayText.EndsWith(ColonString, StringComparison.Ordinal)) { return new TextChange(selectedItem.FilterSpan, displayText.Remove(displayText.Length - ColonString.Length)); } } return new TextChange(selectedItem.FilterSpan, displayText); }
public AvalonEditCompletionData(CompletionItem item) { _item = item; Text = item.DisplayText; Content = item.DisplayText; // Image = item.Glyph; }
/// <summary> /// Creates a new <see cref="CompletionList"/> instance. /// </summary> /// <param name="defaultSpan">The span of the syntax element at the caret position when the <see cref="CompletionList"/> was created.</param> /// <param name="items">The completion items to present to the user.</param> /// <param name="rules">The rules used to control behavior of the completion list shown to the user during typing.</param> /// <param name="suggestionModeItem">An optional <see cref="CompletionItem"/> that appears selected in the list presented to the user during suggestion mode.</param> /// <returns></returns> public static CompletionList Create( TextSpan defaultSpan, ImmutableArray<CompletionItem> items, CompletionRules rules = null, CompletionItem suggestionModeItem = null) { return Create(defaultSpan, items, rules, suggestionModeItem, isExclusive: false); }
/// <summary> /// Creates a new <see cref="CompletionList"/> instance. /// </summary> /// <param name="defaultSpan">The span of the syntax element at the caret position when the <see cref="CompletionList"/> was created.</param> /// <param name="items">The completion items to present to the user.</param> /// <param name="rules">The rules used to control behavior of the completion list shown to the user during typing.</param> /// <param name="suggestionModeItem">An optional <see cref="CompletionItem"/> that appears selected in the list presented to the user during suggestion mode.</param> /// <returns></returns> public static CompletionList Create( TextSpan defaultSpan, ImmutableArray<CompletionItem> items, CompletionRules rules = null, CompletionItem suggestionModeItem = null) { return new CompletionList(defaultSpan, FixItemSpans(items, defaultSpan), rules, suggestionModeItem); }
void ICompletionSet.SetCompletionItems( IList<CompletionItem> completionItems, CompletionItem selectedItem, CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> completionItemFilters, string filterText) { _roslynCompletionSet.SetCompletionItems( completionItems, selectedItem, suggestionModeItem, suggestionMode, isSoftSelected, completionItemFilters, filterText); }
public override TextChange? GetTextChange(CompletionItem selectedItem, char? ch = default(char?), string textTypedSoFar = null) { if (ch.HasValue && ch.Value == '(') { return new TextChange(selectedItem.FilterSpan, ((SymbolCompletionItem)selectedItem).Symbols[0].Name); } return new TextChange(selectedItem.FilterSpan, selectedItem.DisplayText); }
public override bool? IsFilterCharacter(CompletionItem completionItem, char ch, string textTypedSoFar) { if (ch == '!' || ch == '-' || ch == '[') { return true; } return base.IsFilterCharacter(completionItem, ch, textTypedSoFar); }
/// <summary> /// Internal for testing purposes only. /// </summary> internal static async Task<TextChange> GetTextChangeAsync( CompletionService service, Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken)) { var change = await service.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false); // normally the items that produce multiple changes are not expecting to trigger the behaviors that rely on looking at the text return change.TextChange; }
public void AddItem(CompletionItem item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } _itemsBuilder.Add(item); }
private CompletionItem GetCompletionItem(CompletionItem item) { if (item is DescriptionModifyingCompletionItem) { return ((DescriptionModifyingCompletionItem)item).CompletionItem; } return item; }
internal static bool IsFilterCharacter(CompletionItem item, char ch, string textTypedSoFar) { // If the user types something that matches what is in the completion list, then just // count it as a filter character. // For example, if they type "Program " and "Program Files" is in the list, the <space> // should be counted as a filter character and not a commit character. return item.DisplayText.StartsWith(textTypedSoFar, StringComparison.OrdinalIgnoreCase); }
public override TextChange GetTextChange(CompletionItem selectedItem, char? ch = null, string textTypedSoFar = null) { var symbolItem = (SymbolCompletionItem)selectedItem; var insertionText = ch == null ? symbolItem.InsertionText : GetInsertionText(symbolItem, ch.Value); return new TextChange(selectedItem.FilterSpan, insertionText); }
public SimplePresentationItem(CompletionItem item, CompletionService completionService, bool isSuggestionModeItem = false) { Debug.Assert(item != null); Debug.Assert(completionService != null); this.Item = item; this.CompletionService = completionService; this.IsSuggestionModeItem = isSuggestionModeItem; }
public void PresentItems( ITrackingSpan triggerSpan, IList<CompletionItem> completionItems, CompletionItem selectedItem, CompletionItem presetBuilder, bool suggestionMode, bool isSoftSelected) { AssertIsForeground(); // check if this update is still relevant if (_textView.IsClosed || _isDismissed) { return; } _completionSet.SetTrackingSpan(triggerSpan); _ignoreSelectionStatusChangedEvent = true; try { _completionSet.SetCompletionItems(completionItems, selectedItem, presetBuilder, suggestionMode, isSoftSelected); } finally { _ignoreSelectionStatusChangedEvent = false; } if (_editorSessionOpt == null) { // We're tracking the caret. Don't have the editor do it. // Map the span instead of a point to avoid affinity problems. _editorSessionOpt = _completionBroker.CreateCompletionSession( _textView, triggerSpan.GetStartTrackingPoint(PointTrackingMode.Negative), trackCaret: false); var debugTextView = _textView as IDebuggerTextView; if (debugTextView != null && !debugTextView.IsImmediateWindow) { debugTextView.HACK_StartCompletionSession(_editorSessionOpt); } _editorSessionOpt.Dismissed += (s, e) => OnEditorSessionDismissed(); // So here's the deal. We cannot create the editor session and give it the right // items (even though we know what they are). Instead, the session will call // back into the ICompletionSourceProvider (which is us) to get those values. It // will pass itself along with the calls back into ICompletionSourceProvider. // So, in order to make that connection work, we add properties to the session // so that we can call back into ourselves, get the items and add it to the // session. _editorSessionOpt.Properties.AddProperty(Key, this); _editorSessionOpt.Start(); } }
private CompletionProvider GetCompletionProvider(CompletionItem item) { var completionService = this.GetCompletionService() as CompletionServiceWithProviders; if (completionService != null) { return completionService.GetProvider(item); } return null; }
public override bool IsCommitCharacter(CompletionItem completionItem, char ch, string textTypedSoFar) { if ((ch == '"' || ch == ' ') && completionItem.DisplayText.Contains(ch)) { return false; } return CompletionUtilities.IsCommitCharacter(completionItem, ch, textTypedSoFar) || ch == '>' || ch == '\t'; }
protected override bool SendEnterThroughToEditorCore(CompletionItem completionItem, string textTypedSoFar, OptionSet options) { // If the text doesn't match, no reason to even check the options if (completionItem.DisplayText != textTypedSoFar) { return false; } return options.GetOption(CSharpCompletionOptions.AddNewLineOnEnterAfterFullyTypedWord); }
public static DeclarationModifiers GetModifiers(CompletionItem item) { if (item.Properties.TryGetValue("Modifiers", out var text) && DeclarationModifiers.TryParse(text, out var modifiers)) { return modifiers; } return default(DeclarationModifiers); }
public static int GetTokenSpanEnd(CompletionItem item) { if (item.Properties.TryGetValue("TokenSpanEnd", out var text) && int.TryParse(text, out var number)) { return number; } return 0; }
public override Task <CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) { if (!item.Properties.TryGetValue(DescriptionKey, out var description)) { return(SpecializedTasks.Default <CompletionDescription>()); } return(Task.FromResult(CompletionDescription.Create( ImmutableArray.Create(new TaggedText(TextTags.Text, description))))); }
public CommitResult TryCommit(IAsyncCompletionSession session, ITextBuffer buffer, CompletionItem item, char typedChar, CancellationToken token) { return(typedChar.Equals(' ') ? CommitResult.Handled : CommitResult.Unhandled); }
/// <summary> /// Returns true if the completion item should be "soft" selected, or false if it should be "hard" /// selected. /// </summary> public virtual bool ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTrigger trigger) { return(filterText.Length == 0 && !item.Rules.Preselect); }
public IReadOnlyList <TextSpan> GetHighlightedSpans(CompletionItem completionItem, string filterText) { var match = GetMatch(completionItem, filterText, includeMatchSpans: true); return(match?.MatchedSpans); }
private static void AssertRazorCompletionItem(DirectiveDescriptor directive, CompletionItem item, IAsyncCompletionSource source) => AssertRazorCompletionItem(directive.Directive, directive, item, source);
public virtual TextChange GetTextChange(CompletionItem selectedItem, char?ch = null, string textTypedSoFar = null) { return(new TextChange(selectedItem.FilterSpan, selectedItem.DisplayText)); }
public override async Task ProduceCompletionListAsync(CompletionListContext context) { try { var document = context.Document; var position = context.Position; var options = context.Options; var cancellationToken = context.CancellationToken; var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree.IsInNonUserCode(position, cancellationToken)) { return; } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken); if (token.IsMandatoryNamedParameterPosition()) { return; } var typeInferenceService = document.GetLanguageService <ITypeInferenceService>(); var span = new TextSpan(position, 0); var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); var type = typeInferenceService.InferType(semanticModel, position, objectAsDefault: true, cancellationToken: cancellationToken); // If we have a Nullable<T>, unwrap it. if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { type = type.GetTypeArguments().FirstOrDefault(); if (type == null) { return; } } if (type.TypeKind != TypeKind.Enum) { type = GetCompletionListType(type, semanticModel.GetEnclosingNamedType(position, cancellationToken), semanticModel.Compilation); if (type == null) { return; } } if (!type.IsEditorBrowsable(options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language), semanticModel.Compilation)) { return; } // Does type have any aliases? ISymbol alias = await type.FindApplicableAlias(position, semanticModel, cancellationToken).ConfigureAwait(false); var displayService = document.GetLanguageService <ISymbolDisplayService>(); var displayText = alias != null ? alias.Name : displayService.ToMinimalDisplayString(semanticModel, position, type); var workspace = document.Project.Solution.Workspace; var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); var textChangeSpan = CompletionUtilities.GetTextChangeSpan(text, position); var item = new CompletionItem( this, displayText: displayText, filterSpan: textChangeSpan, descriptionFactory: CommonCompletionUtilities.CreateDescriptionFactory(workspace, semanticModel, position, alias ?? type), glyph: (alias ?? type).GetGlyph(), preselect: true, rules: ItemRules.Instance); context.AddItem(item); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
protected abstract bool IsGeneric(CompletionItem completionItem);
public override async Task <CompletionChange> GetChangeAsync(Document document, CompletionItem item, char?commitKey = default(char?), CancellationToken cancellationToken = default(CancellationToken)) { var projectIdGuid = item.Properties[ProjectGuidKey]; var projectId = ProjectId.CreateFromSerialized(new System.Guid(projectIdGuid)); var project = document.Project.Solution.GetProject(projectId); var assemblyName = item.DisplayText; var publicKey = await GetPublicKeyOfProjectAsync(project, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(publicKey)) { assemblyName += $", PublicKey={ publicKey }"; } var textChange = new TextChange(item.Span, assemblyName); return(CompletionChange.Create(textChange)); }
protected override Task <TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char?ch, CancellationToken cancellationToken) { return(Task.FromResult <TextChange?>(new TextChange( selectedItem.Span, selectedItem.DisplayText))); }
protected override async Task <TextChange?> GetTextChangeAsync(CompletionItem selectedItem, char?ch, CancellationToken cancellationToken) { (string beforeText, string afterText, string newMethod) = await GetInsertText(selectedItem.Properties); return(new TextChange(new TextSpan(selectedItem.Span.Start, selectedItem.Span.Length), beforeText + afterText)); }
protected bool IsArgumentName(CompletionItem item) { return(item.Tags.Contains(CompletionTags.ArgumentName)); }
private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, CompletionItem item, IAsyncCompletionSource source) { Assert.Equal(item.DisplayText, completionDisplayText); Assert.Equal(item.FilterText, completionDisplayText); Assert.Equal(item.InsertText, directive.Directive); Assert.Same(item.Source, source); Assert.True(item.Properties.TryGetProperty <DirectiveCompletionDescription>(RazorDirectiveCompletionSource.DescriptionKey, out var actualDescription)); Assert.Equal(directive.Description, actualDescription.Description); AssertRazorCompletionItemDefaults(item); }
public abstract bool IsCommitCharacter(CompletionItem completionItem, char ch, string textTypedSoFar);
public virtual bool IsFilterCharacter(CompletionItem completionItem, char ch, string textTypedSoFar) { return(false); }
public override async Task ProvideCompletionsAsync(CompletionContext context) { // This provider is exported for all workspaces - so limit it to just our workspace & the debugger's intellisense workspace if (context.Document.Project.Solution.Workspace.Kind != WorkspaceKind.AnyCodeRoslynWorkspace && context.Document.Project.Solution.Workspace.Kind != StringConstants.DebuggerIntellisenseWorkspaceKind) { return; } var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return; } var text = await context.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); var completionParams = new LSP.CompletionParams { TextDocument = ProtocolConversions.DocumentToTextDocumentIdentifier(context.Document), Position = ProtocolConversions.LinePositionToPosition(text.Lines.GetLinePosition(context.Position)), Context = new LSP.CompletionContext { TriggerCharacter = context.Trigger.Character.ToString(), TriggerKind = GetTriggerKind(context.Trigger) } }; var completionObject = await lspClient.RequestAsync(LSP.Methods.TextDocumentCompletion.ToLSRequest(), completionParams, context.CancellationToken).ConfigureAwait(false); if (completionObject == null) { return; } var completionList = ((JToken)completionObject).ToObject <RoslynCompletionItem[]>(); foreach (var item in completionList) { ImmutableArray <string> tags; if (item.Tags != null) { tags = item.Tags.AsImmutable(); } else { var glyph = ProtocolConversions.CompletionItemKindToGlyph(item.Kind); tags = GlyphTags.GetTags(glyph); } var properties = ImmutableDictionary.CreateBuilder <string, string>(); if (!string.IsNullOrEmpty(item.Detail)) { // The display text is encoded as TaggedText | value properties.Add("Description", $"Text|{item.Detail}"); } properties.Add("InsertionText", item.InsertText); properties.Add("ResolveData", JToken.FromObject(item).ToString()); var completionItem = CompletionItem.Create(item.Label, item.FilterText, item.SortText, properties: properties.ToImmutable(), tags: tags); context.AddItem(completionItem); } }
public abstract bool SendEnterThroughToEditor(CompletionItem completionItem, string textTypedSoFar);
private void CommitOnNonTypeChar( CompletionItem item, Model model) { Commit(item, model, commitChar: null, initialTextSnapshot: null, nextHandler: null); }
protected override async Task <CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) { var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return(await base.GetDescriptionWorkerAsync(document, item, cancellationToken).ConfigureAwait(false)); } if (!item.Properties.TryGetValue("ResolveData", out var serializedItem)) { return(await base.GetDescriptionWorkerAsync(document, item, cancellationToken).ConfigureAwait(false)); } var completionItem = JToken.Parse(serializedItem).ToObject <RoslynCompletionItem>(); var request = new LspRequest <RoslynCompletionItem, RoslynCompletionItem>(LSP.Methods.TextDocumentCompletionResolveName); var resolvedCompletionItem = await lspClient.RequestAsync(request, completionItem, cancellationToken).ConfigureAwait(false); if (resolvedCompletionItem?.Description == null) { return(await base.GetDescriptionWorkerAsync(document, item, cancellationToken).ConfigureAwait(false)); } var parts = resolvedCompletionItem.Description.Select(tt => tt.ToTaggedText()).AsImmutable(); return(CompletionDescription.Create(parts)); }
public override bool SendEnterThroughToEditor(CompletionItem completionItem, string textTypedSoFar) { return(CompletionUtilities.SendEnterThroughToEditor(completionItem, textTypedSoFar)); }
protected static int GetRecentItemIndex(ImmutableArray <string> recentItems, CompletionItem item) { var index = recentItems.IndexOf(item.DisplayText); return(-index); }
get => GetIcon(CompletionItem, MimeType);
protected bool IsObjectCreationItem(CompletionItem item) { return(item.Tags.Contains(CompletionTags.ObjectCreation)); }
public override async Task ProvideCompletionsAsync(CompletionContext context) { if (!context.Options.GetOption(RegularExpressionsOptions.ProvideRegexCompletions, context.Document.Project.Language)) { return; } if (context.Trigger.Kind != CompletionTriggerKind.Invoke && context.Trigger.Kind != CompletionTriggerKind.InvokeAndCommitIfUnique && context.Trigger.Kind != CompletionTriggerKind.Insertion) { return; } var position = context.Position; var(tree, stringToken) = await _language.TryGetTreeAndTokenAtPositionAsync( context.Document, position, context.CancellationToken).ConfigureAwait(false); if (tree == null || position <= stringToken.SpanStart || position >= stringToken.Span.End) { return; } var embeddedContext = new EmbeddedCompletionContext(this._language, context, tree, stringToken); ProvideCompletions(embeddedContext); if (embeddedContext.Items.Count == 0) { return; } foreach (var embeddedItem in embeddedContext.Items) { var change = embeddedItem.Change; var textChange = change.TextChange; var properties = ImmutableDictionary.CreateBuilder <string, string>(); properties.Add(StartKey, textChange.Span.Start.ToString()); properties.Add(LengthKey, textChange.Span.Length.ToString()); properties.Add(NewTextKey, textChange.NewText); properties.Add(DescriptionKey, embeddedItem.FullDescription); properties.Add(EmbeddedLanguageCompletionProvider.EmbeddedProviderName, this.Name); if (change.NewPosition != null) { properties.Add(NewPositionKey, change.NewPosition.ToString()); } // Keep everything sorted in the order we just produced the items in. var sortText = context.Items.Count.ToString("0000"); context.AddItem(CompletionItem.Create( displayText: embeddedItem.DisplayText, inlineDescription: embeddedItem.InlineDescription, sortText: sortText, properties: properties.ToImmutable(), rules: s_rules)); } context.IsExclusive = true; }
private void Commit( CompletionItem item, Model model, char?commitChar, ITextSnapshot initialTextSnapshot, Action nextHandler) { AssertIsForeground(); // We could only be called if we had a model at this point. Contract.ThrowIfNull(model); // Now that we've captured the model at this point, we can stop ourselves entirely. // This is also desirable as we may have reentrancy problems when we call into // custom commit completion providers. I.e. if the custom provider moves the caret, // then we do not want to process that move as it may put us into an unexpected state. // // TODO(cyrusn): We still have a general reentrancy problem where calling into a custom // commit provider (or just calling into the editor) may cause something to call back // into us. However, for now, we just hope that no such craziness will occur. this.DismissSessionIfActive(); CompletionChange completionChange; using (var transaction = CaretPreservingEditTransaction.TryCreate( EditorFeaturesResources.IntelliSense, TextView, _undoHistoryRegistry, _editorOperationsFactoryService)) { if (transaction == null) { // This text buffer has no undo history and has probably been unmapped. // (Workflow unmaps its projections when losing focus (such as double clicking the completion list)). // Bail on committing completion because we won't be able to find a Document to update either. return; } // We want to merge with any of our other programmatic edits (e.g. automatic brace completion) transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance; var provider = GetCompletionProvider(item) as ICustomCommitCompletionProvider; if (provider != null) { provider.Commit(item, this.TextView, this.SubjectBuffer, model.TriggerSnapshot, commitChar); } else { // Right before calling Commit, we may have passed the commitChar through to the // editor. That was so that undoing completion will get us back to the state we // we would be in if completion had done nothing. However, now that we're going // to actually commit, we want to roll back to where we were before we pushed // commit character into the buffer. This has multiple benefits: // // 1) the buffer is in a state we expect it to be in. i.e. we don't have to // worry about what might have happened (like brace-completion) when the // commit char was inserted. // 2) after we commit the item, we'll pass the commit character again into the // buffer (unless the items asks us not to). By doing this, we can make sure // that things like brace-completion or formatting trigger as we expect them // to. var characterWasSentIntoBuffer = commitChar != null && initialTextSnapshot.Version.VersionNumber != this.SubjectBuffer.CurrentSnapshot.Version.VersionNumber; if (characterWasSentIntoBuffer) { RollbackToBeforeTypeChar(initialTextSnapshot); } // Now, get the change the item wants to make. Note that the change will be relative // to the initial snapshot/document the item was triggered from. We'll map that change // forward, then apply it to our current snapshot. var triggerDocument = model.TriggerDocument; var triggerSnapshot = model.TriggerSnapshot; var completionService = CompletionService.GetService(triggerDocument); Contract.ThrowIfNull(completionService, nameof(completionService)); completionChange = completionService.GetChangeAsync( triggerDocument, item, commitChar, CancellationToken.None).WaitAndGetResult(CancellationToken.None); var textChange = completionChange.TextChange; var triggerSnapshotSpan = new SnapshotSpan(triggerSnapshot, textChange.Span.ToSpan()); var mappedSpan = triggerSnapshotSpan.TranslateTo( this.SubjectBuffer.CurrentSnapshot, SpanTrackingMode.EdgeInclusive); var adjustedNewText = AdjustForVirtualSpace(textChange); var editOptions = GetEditOptions(mappedSpan, adjustedNewText); // Now actually make the text change to the document. using (var textEdit = this.SubjectBuffer.CreateEdit(editOptions, reiteratedVersionNumber: null, editTag: null)) { textEdit.Replace(mappedSpan.Span, adjustedNewText); textEdit.Apply(); } // If the completion change requested a new position for the caret to go, // then set the caret to go directly to that point. if (completionChange.NewPosition.HasValue) { SetCaretPosition(desiredCaretPosition: completionChange.NewPosition.Value); } else if (editOptions.ComputeMinimalChange) { // Or, If we're doing a minimal change, then the edit that we make to the // buffer may not make the total text change that places the caret where we // would expect it to go based on the requested change. In this case, // determine where the item should go and set the care manually. // Note: we only want to move the caret if the caret would have been moved // by the edit. i.e. if the caret was actually in the mapped span that // we're replacing. if (TextView.GetCaretPoint(this.SubjectBuffer) is SnapshotPoint caretPositionInBuffer && mappedSpan.IntersectsWith(caretPositionInBuffer)) { SetCaretPosition(desiredCaretPosition: mappedSpan.Start.Position + adjustedNewText.Length); } } // Now, pass along the commit character unless the completion item said not to if (characterWasSentIntoBuffer && !completionChange.IncludesCommitCharacter) { nextHandler(); } // If the insertion is long enough, the caret will scroll out of the visible area. // Re-center the view. this.TextView.Caret.EnsureVisible(); } transaction.Complete(); } // Let the completion rules know that this item was committed. this.MakeMostRecentItem(item.DisplayText); }
protected override SyntaxToken GetToken(CompletionItem completionItem, SyntaxTree tree, CancellationToken cancellationToken) { var tokenSpanEnd = MemberInsertionCompletionItem.GetTokenSpanEnd(completionItem); return(tree.FindTokenOnLeftOfPosition(tokenSpanEnd, cancellationToken)); }
protected static bool IsEnumMemberItem(CompletionItem item) { return(item.Tags.Contains(CompletionTags.EnumMember)); }
public RoslynCompletionData(Microsoft.CodeAnalysis.Document document, ITextSnapshot triggerSnapshot, CompletionService completionService, CompletionItem completionItem) { this.doc = document; this.triggerSnapshot = triggerSnapshot; this.completionService = completionService; CompletionItem = completionItem; }
public override TextDocumentIdentifier?GetTextDocumentIdentifier(CompletionItem request) => null;
protected int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2) { int diff; diff = PatternMatch.CompareType(match1, match2); if (diff != 0) { return(diff); } diff = PatternMatch.CompareCamelCase(match1, match2); if (diff != 0) { return(diff); } // argument names are not prefered if (IsArgumentName(item1) && !IsArgumentName(item2)) { return(1); } else if (IsArgumentName(item2) && !IsArgumentName(item1)) { return(-1); } // preselected items are prefered if (item1.Rules.Preselect && !item2.Rules.Preselect) { return(-1); } else if (item2.Rules.Preselect && !item1.Rules.Preselect) { return(1); } diff = PatternMatch.CompareCase(match1, match2); if (diff != 0) { return(diff); } diff = PatternMatch.ComparePunctuation(match1, match2); if (diff != 0) { return(diff); } return(0); }