diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 77f4543178928c6e58c1dbfc619e4096041fbca0..91a08e3f711d206587f6666b6adda09bb0084af1 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -199,13 +199,12 @@ - + - @@ -268,7 +267,6 @@ - @@ -475,7 +473,6 @@ - @@ -783,4 +780,4 @@ - + \ No newline at end of file diff --git a/src/EditorFeatures/Core/Extensibility/Completion/CompletionItemEventArgs.cs b/src/EditorFeatures/Core/Extensibility/Completion/CompletionItemEventArgs.cs new file mode 100644 index 0000000000000000000000000000000000000000..ef07a437a7d408adcaf3b3eb928c1ad7c960f538 --- /dev/null +++ b/src/EditorFeatures/Core/Extensibility/Completion/CompletionItemEventArgs.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.CodeAnalysis.Completion; + +namespace Microsoft.CodeAnalysis.Editor +{ + internal class CompletionItemEventArgs : EventArgs + { + public CompletionItem CompletionItem { get; } + + public CompletionItemEventArgs( + CompletionItem completionItem) + { + CompletionItem = completionItem; + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Extensibility/Completion/ICompletionPresenterSession.cs b/src/EditorFeatures/Core/Extensibility/Completion/ICompletionPresenterSession.cs index bbbb118789fb604b7dcf099cf93e0af857f34415..46f9473ee9bbaf2b1af8b595663bd10792665afd 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/ICompletionPresenterSession.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/ICompletionPresenterSession.cs @@ -11,8 +11,8 @@ namespace Microsoft.CodeAnalysis.Editor internal interface ICompletionPresenterSession : IIntelliSensePresenterSession { void PresentItems( - ITrackingSpan triggerSpan, IList items, PresentationItem selectedItem, - PresentationItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, + ITrackingSpan triggerSpan, IList items, CompletionItem selectedItem, + CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, string filterText); @@ -21,8 +21,8 @@ internal interface ICompletionPresenterSession : IIntelliSensePresenterSession void SelectPreviousPageItem(); void SelectNextPageItem(); - event EventHandler ItemSelected; - event EventHandler ItemCommitted; + event EventHandler ItemSelected; + event EventHandler ItemCommitted; event EventHandler FilterStateChanged; } } diff --git a/src/EditorFeatures/Core/Extensibility/Completion/PresentationItem.cs b/src/EditorFeatures/Core/Extensibility/Completion/PresentationItem.cs deleted file mode 100644 index e3b91c3900c9c2d504f1b4a0d1216ed22d9e4e42..0000000000000000000000000000000000000000 --- a/src/EditorFeatures/Core/Extensibility/Completion/PresentationItem.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Completion; - -namespace Microsoft.CodeAnalysis.Editor -{ - internal abstract class PresentationItem - { - public abstract CompletionItem Item { get; } - public abstract bool IsSuggestionModeItem { get; } - public abstract CompletionService CompletionService { get; } - - public abstract Task GetDescriptionAsync(Document document, CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Extensibility/Completion/PresentationItemEventArgs.cs b/src/EditorFeatures/Core/Extensibility/Completion/PresentationItemEventArgs.cs deleted file mode 100644 index 002523ef6bb023e6d91053ad2d167326578ec4d7..0000000000000000000000000000000000000000 --- a/src/EditorFeatures/Core/Extensibility/Completion/PresentationItemEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.CodeAnalysis.Editor -{ - internal class PresentationItemEventArgs : EventArgs - { - public PresentationItem PresentationItem { get; } - - public PresentationItemEventArgs(PresentationItem presentationItem) - { - this.PresentationItem = presentationItem; - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session.cs index 4c61a88fd2c6d91f8e9f2433bc46706a8480f76a..c5b6fc25d71b51f357920089cccc6172a98aa00c 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session.cs @@ -76,20 +76,20 @@ private SnapshotPoint GetCaretPointInViewBuffer() return Controller.GetCaretPointInViewBuffer(); } - private void OnPresenterSessionItemCommitted(object sender, PresentationItemEventArgs e) + private void OnPresenterSessionItemCommitted(object sender, CompletionItemEventArgs e) { AssertIsForeground(); Contract.ThrowIfFalse(ReferenceEquals(this.PresenterSession, sender)); - this.Controller.CommitItem(e.PresentationItem); + this.Controller.CommitItem(e.CompletionItem); } - private void OnPresenterSessionItemSelected(object sender, PresentationItemEventArgs e) + private void OnPresenterSessionItemSelected(object sender, CompletionItemEventArgs e) { AssertIsForeground(); Contract.ThrowIfFalse(ReferenceEquals(this.PresenterSession, sender)); - SetModelSelectedItem(m => e.PresentationItem.IsSuggestionModeItem ? m.DefaultSuggestionModeItem : e.PresentationItem); + SetModelSelectedItem(m => e.CompletionItem); } private void OnPresenterSessionCompletionItemFilterStateChanged( diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_ComputeModel.cs index 39efc94864c6cfd2cae3e19a8f1e8c08d522144c..d611327719e1ad5896061052d2454df97509dd86 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_ComputeModel.cs @@ -106,17 +106,13 @@ private async Task DoInBackgroundAsync(CancellationToken cancellationToke return null; } + var suggestionMode = _useSuggestionMode || completionList.SuggestionModeItem != null; return Model.CreateModel( _documentOpt, _disconnectedBufferGraph, completionList, - selectedItem: completionList.Items.First(), - isHardSelection: false, - isUnique: false, - useSuggestionMode: _useSuggestionMode, - trigger: _trigger, - completionService: _completionService, - workspace: _documentOpt != null ? _documentOpt.Project.Solution.Workspace : null); + useSuggestionMode: suggestionMode, + trigger: _trigger); } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 29746ab539f3c3d2afa0ff8a07f53f9e9a5f4fe8..b4b6dfa36a9f29facf36626a102822b934d8c28b 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -165,13 +165,13 @@ internal partial class Session return model; } - if (ItemIsFilteredOut(currentItem.Item, filterState)) + if (ItemIsFilteredOut(currentItem, filterState)) { continue; } // Check if the item matches the filter text typed so far. - var matchesFilterText = MatchesFilterText(helper, currentItem.Item, filterText, model.Trigger, filterReason, recentItems); + var matchesFilterText = MatchesFilterText(helper, currentItem, filterText, model.Trigger, filterReason, recentItems); if (matchesFilterText) { @@ -243,7 +243,7 @@ private Boolean IsAfterDot(Model model, ITextSnapshot textSnapshot, Dictionary r.MatchedFilterText) - .Select(t => t.PresentationItem.Item) + .Select(t => t.CompletionItem) .AsImmutable(); var chosenItems = service.FilterItems( document, matchingCompletionItems, filterText); @@ -252,12 +252,10 @@ private Boolean IsAfterDot(Model model, ITextSnapshot textSnapshot, Dictionary r.PresentationItem.Item == bestOrFirstCompletionItem).First().PresentationItem; + var bestOrFirstCompletionItem = bestCompletionItem ?? filterResults.First().CompletionItem; var hardSelection = IsHardSelection( - model, bestOrFirstPresentationItem, textSnapshot, helper, filterReason); + model, bestOrFirstCompletionItem, textSnapshot, helper, filterReason); // Determine if we should consider this item 'unique' or not. A unique item // will be automatically committed if the user hits the 'invoke completion' @@ -271,8 +269,8 @@ private Boolean IsAfterDot(Model model, ITextSnapshot textSnapshot, Dictionary 0; - var result = model.WithFilteredItems(filterResults.Select(r => r.PresentationItem).AsImmutable()) - .WithSelectedItem(bestOrFirstPresentationItem) + var result = model.WithFilteredItems(filterResults.Select(r => r.CompletionItem).AsImmutable()) + .WithSelectedItem(bestOrFirstCompletionItem) .WithHardSelection(hardSelection) .WithIsUnique(isUnique); @@ -322,12 +320,12 @@ private Model HandleDeletionTrigger(Model model, List filterResult // (which can happen if the user deletes down to a single character and we // include everything), then we just soft select the first item. - var filteredItems = filterResults.Select(r => r.PresentationItem).AsImmutable(); + var filteredItems = filterResults.Select(r => r.CompletionItem).AsImmutable(); model = model.WithFilteredItems(filteredItems); if (bestFilterResult != null) { - return model.WithSelectedItem(bestFilterResult.Value.PresentationItem) + return model.WithSelectedItem(bestFilterResult.Value.CompletionItem) .WithHardSelection(true) .WithIsUnique(matchCount == 1); } @@ -340,8 +338,8 @@ private Model HandleDeletionTrigger(Model model, List filterResult private bool IsBetterDeletionMatch(FilterResult result1, FilterResult result2) { - var item1 = result1.PresentationItem.Item; - var item2 = result2.PresentationItem.Item; + var item1 = result1.CompletionItem; + var item2 = result2.CompletionItem; var prefixLength1 = item1.FilterText.GetCaseInsensitivePrefixLength(result1.FilterText); var prefixLength2 = item2.FilterText.GetCaseInsensitivePrefixLength(result2.FilterText); @@ -370,13 +368,13 @@ private bool IsBetterDeletionMatch(FilterResult result1, FilterResult result2) private struct FilterResult { - public readonly PresentationItem PresentationItem; + public readonly CompletionItem CompletionItem; public readonly bool MatchedFilterText; public readonly string FilterText; - public FilterResult(PresentationItem presentationItem, string filterText, bool matchedFilterText) + public FilterResult(CompletionItem completionItem, string filterText, bool matchedFilterText) { - PresentationItem = presentationItem; + CompletionItem = completionItem; MatchedFilterText = matchedFilterText; FilterText = filterText; } @@ -402,7 +400,7 @@ public FilterResult(PresentationItem presentationItem, string filterText, bool m // If the user has turned on some filtering states, and we filtered down to // nothing, then we do want the UI to show that to them. That way the user // can turn off filters they don't want and get the right set of items. - return model.WithFilteredItems(ImmutableArray.Empty) + return model.WithFilteredItems(ImmutableArray.Empty) .WithFilterText("") .WithHardSelection(false) .WithIsUnique(false); @@ -503,16 +501,11 @@ private static bool IsAllDigits(string filterText) private bool IsHardSelection( Model model, - PresentationItem bestFilterMatch, + CompletionItem bestFilterMatch, ITextSnapshot textSnapshot, CompletionHelper completionHelper, CompletionFilterReason reason) { - if (model.SuggestionModeItem != null) - { - return bestFilterMatch != null && bestFilterMatch.Item.DisplayText == model.SuggestionModeItem.Item.DisplayText; - } - if (bestFilterMatch == null || model.UseSuggestionMode) { return false; @@ -529,11 +522,11 @@ private static bool IsAllDigits(string filterText) // // Completion will comes up after = with 'integer' selected (Because of MRU). We do // not want 'space' to commit this. - var viewSpan = model.GetViewBufferSpan(bestFilterMatch.Item.Span); + var viewSpan = model.GetViewBufferSpan(bestFilterMatch.Span); var fullFilterText = model.GetCurrentTextInSnapshot(viewSpan, textSnapshot, endPoint: null); var trigger = model.Trigger; - var shouldSoftSelect = ShouldSoftSelectItem(bestFilterMatch.Item, fullFilterText, trigger); + var shouldSoftSelect = ShouldSoftSelectItem(bestFilterMatch, fullFilterText, trigger); if (shouldSoftSelect) { return false; @@ -541,7 +534,7 @@ private static bool IsAllDigits(string filterText) // If the user moved the caret left after they started typing, the 'best' match may not match at all // against the full text span that this item would be replacing. - if (!MatchesFilterText(completionHelper, bestFilterMatch.Item, fullFilterText, trigger, reason, this.Controller.GetRecentItems())) + if (!MatchesFilterText(completionHelper, bestFilterMatch, fullFilterText, trigger, reason, this.Controller.GetRecentItems())) { return false; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelBuilderState.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelBuilderState.cs index 2f88018d24624459771cfec24ba9e11cd5e50a4c..aca0825113b6c35a22d24414ae5ed1d62ee4f1c2 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelBuilderState.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelBuilderState.cs @@ -28,15 +28,17 @@ public void SetModelBuilderState(bool includeBuilder) // already in soft select mode. var softSelect = includeBuilder || model.IsSoftSelection; - // If the selected item is the builder, select the first filtered item instead. - if (model.SelectedItem == model.DefaultSuggestionModeItem) + if (model.SelectedItem == model.SuggestionModeItem && + !includeBuilder) { - return model.WithSelectedItem(model.FilteredItems.First()) - .WithHardSelection(!softSelect); + // Use had the builder selected, but turned off the builder. Switch to the + // first filtered item. + model = model.WithSelectedItem(model.FilteredItems.First()); } - return model.WithHardSelection(!softSelect).WithUseSuggestionMode(includeBuilder); + return model.WithHardSelection(!softSelect) + .WithUseSuggestionMode(includeBuilder); } } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelSelectedItem.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelSelectedItem.cs index 104598dd04cdd16c5b773152c66ae6eaefd0a7c0..eae98395be52af5a3d9dab554875c2313c886b5f 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelSelectedItem.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_SetModelSelectedItem.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis.Completion; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion @@ -11,7 +12,7 @@ internal partial class Controller { internal partial class Session { - private void SetModelSelectedItem(Func selector) + private void SetModelSelectedItem(Func selector) { AssertIsForeground(); @@ -29,7 +30,7 @@ public void SetModelIsHardSelection(bool isHardSelection) private Model SetModelSelectedItemInBackground( Model model, - Func selector) + Func selector) { if (model == null) { @@ -38,7 +39,7 @@ public void SetModelIsHardSelection(bool isHardSelection) // Switch to hard selection. var selectedItem = selector(model); - Contract.ThrowIfFalse(model.TotalItems.Contains(selectedItem) || model.DefaultSuggestionModeItem == selectedItem); + Contract.Assert(model.TotalItems.Contains(selectedItem) || model.SuggestionModeItem == selectedItem); if (model.FilteredItems.Contains(selectedItem)) { @@ -52,7 +53,7 @@ public void SetModelIsHardSelection(bool isHardSelection) { // Item wasn't in the filtered list, so we need to recreate the filtered list // with that item in it. - var filteredItemsSet = new HashSet(model.FilteredItems, + var filteredItemsSet = new HashSet(model.FilteredItems, ReferenceEqualityComparer.Instance); var newFilteredItems = model.TotalItems.Where( @@ -64,4 +65,4 @@ public void SetModelIsHardSelection(bool isHardSelection) } } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.cs index b0177e510bad86b6a3030f08296719f085c09d56..6eb512b2a68c4c9c28fba1e3d9e3685f5eeececf 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.cs @@ -124,13 +124,13 @@ internal override void OnModelUpdated(Model modelOpt) else { var selectedItem = modelOpt.SelectedItem; - var viewSpan = selectedItem == null ? (ViewTextSpan?)null : modelOpt.GetViewBufferSpan(selectedItem.Item.Span); + var viewSpan = selectedItem == null ? (ViewTextSpan?)null : modelOpt.GetViewBufferSpan(selectedItem.Span); var triggerSpan = viewSpan == null ? null : modelOpt.GetCurrentSpanInSnapshot(viewSpan.Value, this.TextView.TextSnapshot) .CreateTrackingSpan(SpanTrackingMode.EdgeInclusive); sessionOpt.PresenterSession.PresentItems( - triggerSpan, modelOpt.FilteredItems, selectedItem, modelOpt.SuggestionModeItem, - this.SubjectBuffer.GetFeatureOnOffOption(EditorCompletionOptions.UseSuggestionMode), + triggerSpan, modelOpt.FilteredItems, selectedItem, + modelOpt.SuggestionModeItem, modelOpt.UseSuggestionMode, modelOpt.IsSoftSelection, modelOpt.CompletionItemFilters, modelOpt.FilterText); } } @@ -237,7 +237,7 @@ private OptionSet GetOptions() : workspace.Options; } - private void CommitItem(PresentationItem item) + private void CommitItem(CompletionItem item) { AssertIsForeground(); @@ -245,8 +245,10 @@ private void CommitItem(PresentationItem item) Contract.ThrowIfNull(this.sessionOpt); Contract.ThrowIfNull(this.sessionOpt.Computation.InitialUnfilteredModel); + var model = sessionOpt.InitialUnfilteredModel; + // If the selected item is the builder, there's not actually any work to do to commit - if (item.IsSuggestionModeItem) + if (item == model.SuggestionModeItem) { this.StopModelComputation(); return; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Backspace.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Backspace.cs index 9691fac72214f8497126d1007f22d8c7158926bf..4cc8c834217de39f3982023e887b3430f6507068 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Backspace.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Backspace.cs @@ -151,16 +151,16 @@ internal bool AllFilterTextsEmpty(Model model, SnapshotPoint caretPoint) private bool IsFilterTextEmpty( Model model, SnapshotPoint caretPoint, - PresentationItem item, + CompletionItem item, Dictionary textSpanToText, Dictionary textSpanToViewSpan) { // Easy first check. See if the caret point is before the start of the item. ViewTextSpan filterSpanInViewBuffer; - if (!textSpanToViewSpan.TryGetValue(item.Item.Span, out filterSpanInViewBuffer)) + if (!textSpanToViewSpan.TryGetValue(item.Span, out filterSpanInViewBuffer)) { - filterSpanInViewBuffer = model.GetViewBufferSpan(item.Item.Span); - textSpanToViewSpan[item.Item.Span] = filterSpanInViewBuffer; + filterSpanInViewBuffer = model.GetViewBufferSpan(item.Span); + textSpanToViewSpan[item.Span] = filterSpanInViewBuffer; } if (caretPoint < filterSpanInViewBuffer.TextSpan.Start) @@ -170,7 +170,7 @@ internal bool AllFilterTextsEmpty(Model model, SnapshotPoint caretPoint) var textSnapshot = caretPoint.Snapshot; - var currentText = model.GetCurrentTextInSnapshot(item.Item.Span, textSnapshot, textSpanToText); + var currentText = model.GetCurrentTextInSnapshot(item.Span, textSnapshot, textSpanToText); var currentTextSpan = new TextSpan(filterSpanInViewBuffer.TextSpan.Start, currentText.Length); return currentText.Length == 0; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_CaretPositionChanged.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_CaretPositionChanged.cs index 3abbfb2128d7309337454c1eab5a6a16667e8382..f51b3fdb1b057a2135b943ac230352b048118fe4 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_CaretPositionChanged.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_CaretPositionChanged.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; @@ -48,7 +49,7 @@ internal override void OnCaretPositionChanged(object sender, EventArgs args) // We want the filter span non-empty because we still want completion in the following case: // A a = new | -> A a = new (| - var currentSpan = model.GetViewBufferSpan(model.SelectedItem.Item.Span).TextSpan; + var currentSpan = model.GetViewBufferSpan(model.SelectedItem.Span).TextSpan; if (caretPoint == currentSpan.Start && currentSpan.Length > 0) { sessionOpt.SetModelIsHardSelection(false); @@ -75,16 +76,16 @@ internal bool IsCaretOutsideAllItemBounds(Model model, SnapshotPoint caretPoint) private bool IsCaretOutsideItemBounds( Model model, SnapshotPoint caretPoint, - PresentationItem item, + CompletionItem item, Dictionary textSpanToText, Dictionary textSpanToViewSpan) { // Easy first check. See if the caret point is before the start of the item. ViewTextSpan filterSpanInViewBuffer; - if (!textSpanToViewSpan.TryGetValue(item.Item.Span, out filterSpanInViewBuffer)) + if (!textSpanToViewSpan.TryGetValue(item.Span, out filterSpanInViewBuffer)) { - filterSpanInViewBuffer = model.GetViewBufferSpan(item.Item.Span); - textSpanToViewSpan[item.Item.Span] = filterSpanInViewBuffer; + filterSpanInViewBuffer = model.GetViewBufferSpan(item.Span); + textSpanToViewSpan[item.Span] = filterSpanInViewBuffer; } if (caretPoint < filterSpanInViewBuffer.TextSpan.Start) @@ -94,10 +95,10 @@ internal bool IsCaretOutsideAllItemBounds(Model model, SnapshotPoint caretPoint) var textSnapshot = caretPoint.Snapshot; - var currentText = model.GetCurrentTextInSnapshot(item.Item.Span, textSnapshot, textSpanToText); + var currentText = model.GetCurrentTextInSnapshot(item.Span, textSnapshot, textSpanToText); var currentTextSpan = new TextSpan(filterSpanInViewBuffer.TextSpan.Start, currentText.Length); return !currentTextSpan.IntersectsWith(caretPoint); } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs index 3144afc3b4b961a6cdb863facd9510813ef6f68f..b6f713a3e01de76de8adae67ec0185b967a43e86 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs @@ -27,13 +27,13 @@ private CompletionProvider GetCompletionProvider(CompletionItem item) } private void CommitOnNonTypeChar( - PresentationItem item, Model model) + CompletionItem item, Model model) { Commit(item, model, commitChar: null, initialTextSnapshot: null, nextHandler: null); } private void Commit( - PresentationItem item, Model model, char? commitChar, + CompletionItem item, Model model, char? commitChar, ITextSnapshot initialTextSnapshot, Action nextHandler) { AssertIsForeground(); @@ -58,10 +58,10 @@ private CompletionProvider GetCompletionProvider(CompletionItem item) // We want to merge with any of our other programmatic edits (e.g. automatic brace completion) transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance; - var provider = GetCompletionProvider(item.Item) as ICustomCommitCompletionProvider; + var provider = GetCompletionProvider(item) as ICustomCommitCompletionProvider; if (provider != null) { - provider.Commit(item.Item, this.TextView, this.SubjectBuffer, model.TriggerSnapshot, commitChar); + provider.Commit(item, this.TextView, this.SubjectBuffer, model.TriggerSnapshot, commitChar); } else { @@ -95,7 +95,7 @@ private CompletionProvider GetCompletionProvider(CompletionItem item) Contract.ThrowIfNull(completionService, nameof(completionService)); completionChange = completionService.GetChangeAsync( - triggerDocument, item.Item, commitChar, CancellationToken.None).WaitAndGetResult(CancellationToken.None); + triggerDocument, item, commitChar, CancellationToken.None).WaitAndGetResult(CancellationToken.None); var textChange = completionChange.TextChange; var triggerSnapshotSpan = new SnapshotSpan(triggerSnapshot, textChange.Span.ToSpan()); @@ -133,7 +133,7 @@ private CompletionProvider GetCompletionProvider(CompletionItem item) } // Let the completion rules know that this item was committed. - this.MakeMostRecentItem(item.Item.DisplayText); + this.MakeMostRecentItem(item.DisplayText); } private void RollbackToBeforeTypeChar(ITextSnapshot initialTextSnapshot) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs index 1f90e91c2d3639a19527fc819c7eb4cf3bda629b..b32cfed8b891bba05319a4ea27c4ad9438dd2101 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_ReturnKey.cs @@ -76,7 +76,7 @@ private void CommitOnEnter(out bool sendThrough, out bool committed) } // If the selected item is the builder, dismiss - if (model.SelectedItem.IsSuggestionModeItem) + if (model.SelectedItem == model.SuggestionModeItem) { sendThrough = false; committed = false; @@ -86,13 +86,13 @@ private void CommitOnEnter(out bool sendThrough, out bool committed) if (sendThrough) { // Get the text that the user has currently entered into the buffer - var viewSpan = model.GetViewBufferSpan(model.SelectedItem.Item.Span); + var viewSpan = model.GetViewBufferSpan(model.SelectedItem.Span); var textTypedSoFar = model.GetCurrentTextInSnapshot( viewSpan, this.TextView.TextSnapshot, this.GetCaretPointInViewBuffer()); var service = GetCompletionService(); sendThrough = SendEnterThroughToEditor( - service.GetRules(), model.SelectedItem.Item, textTypedSoFar); + service.GetRules(), model.SelectedItem, textTypedSoFar); } this.CommitOnNonTypeChar(model.SelectedItem, model); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TabKey.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TabKey.cs index f6d9aed1fb15917fa4605558d19cce14b372cc2d..bd5f94bc4207c2a4f611dc27bb5ef513df94a71c 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TabKey.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TabKey.cs @@ -170,7 +170,7 @@ private void CommitOnTab(out bool committed) } // If the selected item is the builder, there's not actually any work to do to commit - if (model.SelectedItem.IsSuggestionModeItem) + if (model.SelectedItem == model.SuggestionModeItem) { committed = true; this.StopModelComputation(); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs index 498c44b97eb8c4d8280f1174def51533920c879f..7c92ab9e645bb3a94d17dca91e4760df12d9c3e0 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_TypeChar.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.Commands; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -311,7 +309,7 @@ private bool IsCommitCharacter(char ch) return false; } - if (model.SelectedItem.IsSuggestionModeItem) + if (model.SelectedItem == model.SuggestionModeItem) { return char.IsLetterOrDigit(ch); } @@ -322,9 +320,9 @@ private bool IsCommitCharacter(char ch) return false; } - var textTypedSoFar = GetTextTypedSoFar(model, model.SelectedItem.Item); + var textTypedSoFar = GetTextTypedSoFar(model, model.SelectedItem); return IsCommitCharacter( - completionService.GetRules(), model.SelectedItem.Item, ch, textTypedSoFar); + completionService.GetRules(), model.SelectedItem, ch, textTypedSoFar); } /// @@ -378,13 +376,13 @@ private bool IsFilterCharacter(char ch) return false; } - if (model.SelectedItem.IsSuggestionModeItem) + if (model.SelectedItem == model.SuggestionModeItem) { return char.IsLetterOrDigit(ch); } - var textTypedSoFar = GetTextTypedSoFar(model, model.SelectedItem.Item); - return IsFilterCharacter(model.SelectedItem.Item, ch, textTypedSoFar); + var textTypedSoFar = GetTextTypedSoFar(model, model.SelectedItem); + return IsFilterCharacter(model.SelectedItem, ch, textTypedSoFar); } private static bool TextTypedSoFarMatchesItem(CompletionItem item, char ch, string textTypedSoFar) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs deleted file mode 100644 index 8dfb3eeef8c76b75e0e2c338ad33c8ea4b451046..0000000000000000000000000000000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/DescriptionModifyingPresentationItem.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Snippets; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion -{ - internal class DescriptionModifyingPresentationItem : PresentationItem - { - public override CompletionItem Item { get; } - public override bool IsSuggestionModeItem { get; } - public override CompletionService CompletionService { get; } - - public DescriptionModifyingPresentationItem(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 override async Task GetDescriptionAsync(Document document, CancellationToken cancellationToken) - { - var languageServices = document.Project.LanguageServices; - var snippetService = languageServices.GetService(); - - var description = await this.CompletionService.GetDescriptionAsync(document, this.Item, cancellationToken).ConfigureAwait(false); - var parts = description.TaggedParts; - - var change = await GetTextChangeAsync(this.CompletionService, document, this.Item, '\t').ConfigureAwait(false); - var insertionText = change.NewText; - - var note = string.Empty; - if (snippetService != null && snippetService.SnippetShortcutExists_NonBlocking(insertionText)) - { - note = string.Format(FeaturesResources.Note_colon_Tab_twice_to_insert_the_0_snippet, insertionText); - } - - if (!string.IsNullOrEmpty(note)) - { - if (parts.Any()) - { - parts = parts.Add(new TaggedText(TextTags.LineBreak, Environment.NewLine)); - } - - parts = parts.Add(new TaggedText(TextTags.Text, note)); - } - - return description.WithTaggedParts(parts); - } - - /// - /// Internal for testing purposes only. - /// - internal static async Task 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; - } - } -} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Model.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Model.cs index 12d466747a77f2eba03aa70509fb432f220241b6..5830c23a08d44c0acab73f522160fbe08fd3df8c 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Model.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Model.cs @@ -20,10 +20,9 @@ internal class Model public Document TriggerDocument { get; } public CompletionList OriginalList { get; } - public ImmutableArray TotalItems { get; } - public ImmutableArray FilteredItems { get; } + public ImmutableArray FilteredItems { get; } - public PresentationItem SelectedItem { get; } + public CompletionItem SelectedItem { get; } public ImmutableArray CompletionItemFilters { get; } public ImmutableDictionary FilterState { get; } @@ -32,14 +31,16 @@ internal class Model public bool IsHardSelection { get; } public bool IsUnique { get; } - // The item the model will use to represent selecting and interacting with when in suggestion mode. - // All models always have this property set. - public PresentationItem DefaultSuggestionModeItem { get; } + /// + /// SuggestionMode item is the "builder" we would display in intellisense. It's + /// always non-null, but will only be shown if is true. + /// If it provided by some then we + /// will use that. Otherwise, we'll have a simple empty-default item that we'll use. + /// + public CompletionItem SuggestionModeItem { get; } + public bool UseSuggestionMode { get; } - // The suggestion mode item, if any, provided by the completion service. - public PresentationItem SuggestionModeItem { get; } public CompletionTrigger Trigger { get; } - public bool UseSuggestionMode { get; } // When committing a completion item, the span replaced ends at this point. public ITrackingPoint CommitTrackingSpanEndPoint { get; } @@ -50,27 +51,25 @@ internal class Model Document triggerDocument, DisconnectedBufferGraph disconnectedBufferGraph, CompletionList originalList, - ImmutableArray totalItems, - ImmutableArray filteredItems, - PresentationItem selectedItem, + ImmutableArray filteredItems, + CompletionItem selectedItem, ImmutableArray completionItemFilters, ImmutableDictionary filterState, string filterText, bool isHardSelection, bool isUnique, bool useSuggestionMode, - PresentationItem suggestionModeItem, - PresentationItem defaultSuggestionModeItem, + CompletionItem suggestionModeItem, CompletionTrigger trigger, ITrackingPoint commitSpanEndPoint, bool dismissIfEmpty) { - Contract.ThrowIfFalse(totalItems.Length != 0, "Must have at least one item."); + Contract.ThrowIfFalse(originalList.Items.Length != 0, "Must have at least one item."); + Contract.ThrowIfNull(selectedItem); this.TriggerDocument = triggerDocument; _disconnectedBufferGraph = disconnectedBufferGraph; this.OriginalList = originalList; - this.TotalItems = totalItems; this.FilteredItems = filteredItems; this.FilterState = filterState; this.SelectedItem = selectedItem; @@ -78,30 +77,24 @@ internal class Model this.FilterText = filterText; this.IsHardSelection = isHardSelection; this.IsUnique = isUnique; - this.UseSuggestionMode = useSuggestionMode; - this.SuggestionModeItem = suggestionModeItem; - this.DefaultSuggestionModeItem = defaultSuggestionModeItem; this.Trigger = trigger; this.CommitTrackingSpanEndPoint = commitSpanEndPoint; this.DismissIfEmpty = dismissIfEmpty; + + this.UseSuggestionMode = useSuggestionMode; + this.SuggestionModeItem = suggestionModeItem ?? CreateDefaultSuggestionModeItem(); } public static Model CreateModel( Document triggerDocument, DisconnectedBufferGraph disconnectedBufferGraph, CompletionList originalList, - CompletionItem selectedItem, - bool isHardSelection, - bool isUnique, bool useSuggestionMode, - CompletionTrigger trigger, - CompletionService completionService, - Workspace workspace) + CompletionTrigger trigger) { - ImmutableArray totalItems; - CompletionItem suggestionModeItem = originalList.SuggestionModeItem; - PresentationItem suggestionModePresentationItem; - PresentationItem defaultSuggestionModePresentationItem; + var selectedItem = originalList.Items.First(); + var isHardSelection = false; + var isUnique = false; // Get the set of actual filters used by all the completion items // that are in the list. @@ -126,58 +119,26 @@ internal class Model // By default we do not filter anything out. ImmutableDictionary filterState = null; - if (completionService != null && - workspace != null && - workspace.Kind != WorkspaceKind.Interactive && // TODO (https://github.com/dotnet/roslyn/issues/5107): support in interactive - workspace.Options.GetOption(InternalFeatureOnOffOptions.Snippets) && - trigger.Kind != CompletionTriggerKind.Snippets) - { - // In order to add snippet expansion notes to completion item descriptions, update - // all of the provided CompletionItems to DescriptionModifyingCompletionItem which will proxy - // requests to the original completion items and add the snippet expansion note to - // the description if necessary. We won't do this if the list was triggered to show - // snippet shortcuts. - - var totalItemsBuilder = ArrayBuilder.GetInstance(); - foreach (var item in originalList.Items) - { - totalItemsBuilder.Add(new DescriptionModifyingPresentationItem(item, completionService)); - } - - totalItems = totalItemsBuilder.ToImmutableAndFree(); - defaultSuggestionModePresentationItem = new DescriptionModifyingPresentationItem( - CreateDefaultSuggestionModeItem(), completionService, isSuggestionModeItem: true); - suggestionModePresentationItem = suggestionModeItem != null ? new DescriptionModifyingPresentationItem(suggestionModeItem, completionService, isSuggestionModeItem: true) : null; - } - else - { - totalItems = originalList.Items.Select(item => new SimplePresentationItem(item, completionService)).ToImmutableArray(); - defaultSuggestionModePresentationItem = new SimplePresentationItem(CreateDefaultSuggestionModeItem(), completionService, isSuggestionModeItem: true); - suggestionModePresentationItem = suggestionModeItem != null ? new SimplePresentationItem(suggestionModeItem, completionService, isSuggestionModeItem: true) : null; - } - - var selectedPresentationItem = totalItems.FirstOrDefault(it => it.Item == selectedItem); - return new Model( triggerDocument, disconnectedBufferGraph, originalList, - totalItems, - totalItems, - selectedPresentationItem, + originalList.Items, + selectedItem, actualItemFilters, filterState, "", isHardSelection, isUnique, useSuggestionMode, - suggestionModePresentationItem, - defaultSuggestionModePresentationItem, + originalList.SuggestionModeItem, trigger, GetDefaultTrackingSpanEnd(originalList.Span, disconnectedBufferGraph), originalList.Rules.DismissIfEmpty); } + public ImmutableArray TotalItems => this.OriginalList.Items; + private static ITrackingPoint GetDefaultTrackingSpanEnd( TextSpan defaultTrackingSpanInSubjectBuffer, DisconnectedBufferGraph disconnectedBufferGraph) @@ -188,10 +149,7 @@ internal class Model PointTrackingMode.Positive); } - private static CompletionItem CreateDefaultSuggestionModeItem() - { - return CompletionItem.Create(displayText: ""); - } + private static CompletionItem CreateDefaultSuggestionModeItem() => CompletionItem.Create(displayText: ""); public bool IsSoftSelection { @@ -202,14 +160,14 @@ public bool IsSoftSelection } private Model With( - Optional> filteredItems = default(Optional>), - Optional selectedItem = default(Optional), + Optional> filteredItems = default(Optional>), + Optional selectedItem = default(Optional), Optional> filterState = default(Optional>), Optional filterText = default(Optional), Optional isHardSelection = default(Optional), Optional isUnique = default(Optional), Optional useSuggestionMode = default(Optional), - Optional suggestionModeItem = default(Optional), + Optional suggestionModeItem = default(Optional), Optional commitTrackingSpanEndPoint = default(Optional)) { var newFilteredItems = filteredItems.HasValue ? filteredItems.Value : FilteredItems; @@ -236,18 +194,18 @@ public bool IsSoftSelection } return new Model( - TriggerDocument, _disconnectedBufferGraph, OriginalList, TotalItems, newFilteredItems, + TriggerDocument, _disconnectedBufferGraph, OriginalList, newFilteredItems, newSelectedItem, CompletionItemFilters, newFilterState, newFilterText, newIsHardSelection, newIsUnique, newUseSuggestionMode, newSuggestionModeItem, - DefaultSuggestionModeItem, Trigger, newCommitTrackingSpanEndPoint, DismissIfEmpty); + Trigger, newCommitTrackingSpanEndPoint, DismissIfEmpty); } - public Model WithFilteredItems(ImmutableArray filteredItems) + public Model WithFilteredItems(ImmutableArray filteredItems) { return With(filteredItems: filteredItems, selectedItem: filteredItems.FirstOrDefault()); } - public Model WithSelectedItem(PresentationItem selectedItem) + public Model WithSelectedItem(CompletionItem selectedItem) { return With(selectedItem: selectedItem); } @@ -262,7 +220,7 @@ public Model WithIsUnique(bool isUnique) return With(isUnique: isUnique); } - public Model WithSuggestionModeItem(PresentationItem suggestionModeItem) + public Model WithSuggestionModeItem(CompletionItem suggestionModeItem) { return With(suggestionModeItem: suggestionModeItem); } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs index 6831988216789bd1eec0c7a87fdcccb5817a7321..08a79aa710218a43a509a294bdd95f3fd986c496 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs @@ -21,11 +21,10 @@ internal sealed class CompletionPresenterSession : ForegroundThreadAffinitizedOb internal readonly IGlyphService GlyphService; private readonly ITextView _textView; - private readonly ITextBuffer _subjectBuffer; public event EventHandler Dismissed; - public event EventHandler ItemCommitted; - public event EventHandler ItemSelected; + public event EventHandler ItemCommitted; + public event EventHandler ItemSelected; public event EventHandler FilterStateChanged; private readonly ICompletionSet _completionSet; @@ -38,10 +37,7 @@ internal sealed class CompletionPresenterSession : ForegroundThreadAffinitizedOb // this scenario. private bool _isDismissed; - public ITextBuffer SubjectBuffer - { - get { return _subjectBuffer; } - } + public ITextBuffer SubjectBuffer { get; } public CompletionPresenterSession( ICompletionSetFactory completionSetFactory, @@ -53,7 +49,7 @@ public ITextBuffer SubjectBuffer _completionBroker = completionBroker; this.GlyphService = glyphService; _textView = textView; - _subjectBuffer = subjectBuffer; + SubjectBuffer = subjectBuffer; _completionSet = completionSetFactory.CreateCompletionSet(this, textView, subjectBuffer); _completionSet.SelectionStatusChanged += OnCompletionSetSelectionStatusChanged; @@ -61,9 +57,9 @@ public ITextBuffer SubjectBuffer public void PresentItems( ITrackingSpan triggerSpan, - IList completionItems, - PresentationItem selectedItem, - PresentationItem suggestionModeItem, + IList completionItems, + CompletionItem selectedItem, + CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, @@ -132,13 +128,14 @@ private void OnEditorSessionDismissed() this.Dismissed?.Invoke(this, new EventArgs()); } - internal void OnCompletionItemCommitted(PresentationItem presentationItem) + internal void OnCompletionItemCommitted(CompletionItem completionItem) { AssertIsForeground(); - this.ItemCommitted?.Invoke(this, new PresentationItemEventArgs(presentationItem)); + this.ItemCommitted?.Invoke(this, new CompletionItemEventArgs(completionItem)); } - private void OnCompletionSetSelectionStatusChanged(object sender, ValueChangedEventArgs eventArgs) + private void OnCompletionSetSelectionStatusChanged( + object sender, ValueChangedEventArgs eventArgs) { AssertIsForeground(); @@ -147,11 +144,10 @@ private void OnCompletionSetSelectionStatusChanged(object sender, ValueChangedEv return; } - var item = _completionSet.GetPresentationItem(eventArgs.NewValue.Completion); - var itemSelected = this.ItemSelected; - if (itemSelected != null && item != null) + var item = _completionSet.GetCompletionItem(eventArgs.NewValue.Completion); + if (item != null) { - itemSelected(this, new PresentationItemEventArgs(item)); + this.ItemSelected?.Invoke(this, new CompletionItemEventArgs(item)); } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CustomCommitCompletion.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CustomCommitCompletion.cs index c4c010cb55f84f10d24c8a00ef3f00c0639393d0..861a15d46f82cff94d952d8000cc1519e5065b1f 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CustomCommitCompletion.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CustomCommitCompletion.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; @@ -16,21 +17,23 @@ internal sealed class CustomCommitCompletion : Completion3, ICustomCommit { private static readonly string s_glyphCompletionWarning = "GlyphCompletionWarning"; private readonly CompletionPresenterSession _completionPresenterSession; - internal readonly PresentationItem PresentationItem; + internal readonly CompletionItem CompletionItem; private readonly ImageMoniker _imageMoniker; + private readonly string _displayText; public CustomCommitCompletion( - CompletionPresenterSession completionPresenterSession, - PresentationItem presentationItem) - : base() + CompletionPresenterSession completionPresenterSession, + CompletionItem completionItem, + string displayText) { // PERF: Note that the base class contains a constructor taking the displayText string // but we're intentionally NOT using that here because it allocates a private CompletionState // object. By overriding the public property getters (DisplayText, InsertionText, etc.) the // extra allocation is avoided. _completionPresenterSession = completionPresenterSession; - this.PresentationItem = presentationItem; - _imageMoniker = ImageMonikers.GetImageMoniker(PresentationItem.Item.Tags); + this.CompletionItem = completionItem; + _displayText = displayText ?? completionItem.DisplayText; + _imageMoniker = ImageMonikers.GetImageMoniker(CompletionItem.Tags); } public void Commit() @@ -38,24 +41,12 @@ public void Commit() // If a commit happens through the UI then let the session know. It will, in turn, // let the underlying controller know, and the controller can commit the completion // item. - _completionPresenterSession.OnCompletionItemCommitted(PresentationItem); + _completionPresenterSession.OnCompletionItemCommitted(CompletionItem); } - public override string DisplayText - { - get - { - return this.PresentationItem.Item.DisplayText; - } - } + public override string DisplayText => _displayText; - public override string InsertionText - { - get - { - return this.DisplayText; // [sic] Same as DisplayText - } - } + public override string InsertionText => DisplayText; public override string Description { @@ -74,7 +65,8 @@ public override string Description public async Task GetDescriptionAsync(CancellationToken cancellationToken) { var document = await GetDocumentAsync(cancellationToken).ConfigureAwait(false); - return await this.PresentationItem.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); + var service = CompletionService.GetService(document); + return await service.GetDescriptionAsync(document, this.CompletionItem, cancellationToken).ConfigureAwait(false); } private Task GetDocumentAsync(CancellationToken cancellationToken) @@ -97,11 +89,11 @@ public override string IconAutomationText } } - public override System.Collections.Generic.IEnumerable AttributeIcons + public override IEnumerable AttributeIcons { get { - if (this.PresentationItem.Item.Tags.Contains(CompletionTags.Warning)) + if (this.CompletionItem.Tags.Contains(CompletionTags.Warning)) { return new[] { new CompletionIcon2(Glyph.CompletionWarning.GetImageMoniker(), s_glyphCompletionWarning, s_glyphCompletionWarning) }; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs index 4105f1226bb1fe7dbd5f1905708e41f5838c9a2d..30320a2f440110acac149a220ddb9b187547662b 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs @@ -16,13 +16,13 @@ internal interface ICompletionSet CompletionSet CompletionSet { get; } void SetCompletionItems( - IList completionItems, - PresentationItem selectedItem, - PresentationItem presetBuilder, + IList completionItems, + CompletionItem selectedItem, + CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, string filterText); - PresentationItem GetPresentationItem(VSCompletion completion); + CompletionItem GetCompletionItem(VSCompletion completion); } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/Roslyn14CompletionSet.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/Roslyn14CompletionSet.cs index 446deb6b7b83d00d774438bf2a69b41991d6c49a..44c8cf37c0a983ada57d6955170ce32c4a2269c6 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/Roslyn14CompletionSet.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/Roslyn14CompletionSet.cs @@ -31,7 +31,9 @@ internal class Roslyn14CompletionSet : ForegroundThreadAffinitizedObject protected readonly ITextBuffer SubjectBuffer; protected readonly CompletionPresenterSession CompletionPresenterSession; - protected Dictionary PresentationItemMap; + + protected Dictionary CompletionItemMap; + protected CompletionItem SuggestionModeItem; protected string FilterText; @@ -57,9 +59,9 @@ public void SetTrackingSpan(ITrackingSpan trackingSpan) } public void SetCompletionItems( - IList completionItems, - PresentationItem selectedItem, - PresentationItem presetBuilder, + IList completionItems, + CompletionItem selectedItem, + CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, @@ -67,54 +69,51 @@ public void SetTrackingSpan(ITrackingSpan trackingSpan) { this.AssertIsForeground(); - VSCompletion selectedCompletionItem = null; - // Initialize the completion map to a reasonable default initial size (+1 for the builder) - PresentationItemMap = PresentationItemMap ?? new Dictionary(completionItems.Count + 1); + CompletionItemMap = CompletionItemMap ?? new Dictionary(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); + } + private void CreateCompletionListBuilder( + CompletionItem selectedItem, + CompletionItem suggestionModeItem, + bool suggestionMode) + { try { VsCompletionSet.WritableCompletionBuilders.BeginBulkOperation(); VsCompletionSet.WritableCompletionBuilders.Clear(); - this.SetupFilters(completionItemFilters); - - var applicableToText = VsCompletionSet.ApplicableTo.GetText( - VsCompletionSet.ApplicableTo.TextBuffer.CurrentSnapshot); - - SimplePresentationItem filteredSuggestionModeItem = null; - if (selectedItem != null) + if (suggestionMode) { - var completionItem = CompletionItem.Create(displayText: applicableToText); - completionItem.Span = VsCompletionSet.ApplicableTo.GetSpan( - VsCompletionSet.ApplicableTo.TextBuffer.CurrentSnapshot).Span.ToTextSpan(); - - filteredSuggestionModeItem = new SimplePresentationItem( - completionItem, - selectedItem.CompletionService, - isSuggestionModeItem: true); - } - - var showBuilder = suggestionMode || presetBuilder != null; - var bestSuggestionModeItem = applicableToText.Length > 0 ? filteredSuggestionModeItem : presetBuilder ?? filteredSuggestionModeItem; + var applicableToText = VsCompletionSet.ApplicableTo.GetText( + VsCompletionSet.ApplicableTo.TextBuffer.CurrentSnapshot); - if (showBuilder && bestSuggestionModeItem != null) - { - var suggestionModeCompletion = GetVSCompletion(bestSuggestionModeItem); - VsCompletionSet.WritableCompletionBuilders.Add(suggestionModeCompletion); - - if (selectedItem != null && selectedItem.IsSuggestionModeItem) - { - selectedCompletionItem = suggestionModeCompletion; - } + var text = applicableToText.Length > 0 ? applicableToText : suggestionModeItem.DisplayText; + var vsCompletion = GetVSCompletion(suggestionModeItem, text); + + VsCompletionSet.WritableCompletionBuilders.Add(vsCompletion); } } finally { VsCompletionSet.WritableCompletionBuilders.EndBulkOperation(); } + } + private void CreateNormalCompletionListItems(IList completionItems) + { try { VsCompletionSet.WritableCompletions.BeginBulkOperation(); @@ -124,45 +123,35 @@ public void SetTrackingSpan(ITrackingSpan trackingSpan) { var completionItem = GetVSCompletion(item); VsCompletionSet.WritableCompletions.Add(completionItem); - - if (item == selectedItem) - { - selectedCompletionItem = completionItem; - } } } finally { VsCompletionSet.WritableCompletions.EndBulkOperation(); } - - VsCompletionSet.SelectionStatus = new CompletionSelectionStatus( - selectedCompletionItem, isSelected: !isSoftSelected, isUnique: selectedCompletionItem != null); } protected virtual void SetupFilters(ImmutableArray completionItemFilters) { } - private VSCompletion GetVSCompletion(PresentationItem item) + private VSCompletion GetVSCompletion(CompletionItem item, string displayText = null) { VSCompletion value; - if (!PresentationItemMap.TryGetValue(item, out value)) + if (!CompletionItemMap.TryGetValue(item, out value)) { - value = new CustomCommitCompletion( - CompletionPresenterSession, - item); - PresentationItemMap.Add(item, value); + value = new CustomCommitCompletion(CompletionPresenterSession, item, displayText); + CompletionItemMap.Add(item, value); } return value; } - public PresentationItem GetPresentationItem(VSCompletion completion) + public CompletionItem GetCompletionItem(VSCompletion completion) { // Linear search is ok since this is only called by the user manually selecting // an item. Creating a reverse mapping uses too much memory and affects GCs. - foreach (var kvp in PresentationItemMap) + foreach (var kvp in CompletionItemMap) { if (kvp.Value == completion) { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSet.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSet.cs index e34d605d79ea75822ef30c24e55f9cb554f7b0a6..ea21e53268d495425740575e1d3a1b86a71f1680 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSet.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSet.cs @@ -51,14 +51,18 @@ void ICompletionSet.SetTrackingSpan(ITrackingSpan trackingSpan) _roslynCompletionSet.SetTrackingSpan(trackingSpan); } - void ICompletionSet.SetCompletionItems(IList completionItems, PresentationItem selectedItem, PresentationItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, string filterText) + void ICompletionSet.SetCompletionItems( + IList completionItems, CompletionItem selectedItem, + CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, string filterText) { - _roslynCompletionSet.SetCompletionItems(completionItems, selectedItem, presetBuilder, suggestionMode, isSoftSelected, completionItemFilters, filterText); + _roslynCompletionSet.SetCompletionItems( + completionItems, selectedItem, suggestionModeItem, suggestionMode, + isSoftSelected, completionItemFilters, filterText); } - PresentationItem ICompletionSet.GetPresentationItem(VSCompletion completion) + CompletionItem ICompletionSet.GetCompletionItem(VSCompletion completion) { - return _roslynCompletionSet.GetPresentationItem(completion); + return _roslynCompletionSet.GetCompletionItem(completion); } #endregion diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/SimplePresentationItem.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/SimplePresentationItem.cs deleted file mode 100644 index c8bbc1d2e10acd95ff92a400ace3202a72b302da..0000000000000000000000000000000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/SimplePresentationItem.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Completion; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion -{ - internal class SimplePresentationItem : PresentationItem - { - public override CompletionItem Item { get; } - public override bool IsSuggestionModeItem { get; } - public override CompletionService CompletionService { get; } - - 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 override Task GetDescriptionAsync(Document document, CancellationToken cancellationToken) - { - return this.CompletionService.GetDescriptionAsync(document, this.Item, cancellationToken); - } - } -} diff --git a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs index 1e43094203f020a616355b6f01f4892f0753af90..9e834f14dfcb1117cabb3b7ba6b9ed1799c6eaf9 100644 --- a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs @@ -76,12 +76,12 @@ public IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) var completionHelper = this.GetCompletionHelper(); if (completionHelper != null) { - var presentationItem = this.PresentationItemMap.Keys.FirstOrDefault(k => k.Item.DisplayText == displayText); + var completionItem = this.CompletionItemMap.Keys.FirstOrDefault(k => k.DisplayText == displayText); - if (presentationItem != null && !presentationItem.IsSuggestionModeItem) + if (completionItem != null && completionItem != SuggestionModeItem) { var highlightedSpans = completionHelper.GetHighlightedSpans( - presentationItem.Item, FilterText, CultureInfo.CurrentCulture); + completionItem, FilterText, CultureInfo.CurrentCulture); if (highlightedSpans != null) { return highlightedSpans.Select(s => s.ToSpan()).ToArray(); diff --git a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSet.cs b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSet.cs index 0bfbcbdeb82fb1c69fad076c42eee4819fe40277..5377fc6065ee3d076fc4a01d3f34bd72eebaae0a 100644 --- a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSet.cs +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSet.cs @@ -77,14 +77,14 @@ void ICompletionSet.SetTrackingSpan(ITrackingSpan trackingSpan) _roslynCompletionSet.SetTrackingSpan(trackingSpan); } - void ICompletionSet.SetCompletionItems(IList completionItems, PresentationItem selectedItem, PresentationItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, string filterText) + void ICompletionSet.SetCompletionItems(IList completionItems, CompletionItem selectedItem, CompletionItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, string filterText) { _roslynCompletionSet.SetCompletionItems(completionItems, selectedItem, presetBuilder, suggestionMode, isSoftSelected, completionItemFilters, filterText); } - PresentationItem ICompletionSet.GetPresentationItem(VSCompletion completion) + CompletionItem ICompletionSet.GetCompletionItem(VSCompletion completion) { - return _roslynCompletionSet.GetPresentationItem(completion); + return _roslynCompletionSet.GetCompletionItem(completion); } #endregion diff --git a/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs index 831f7f14b0e4e61003230fa74d5cb23d14453cea..cb4dbaad100f1fe8a8f630af2a31a65c53aa0e62 100644 --- a/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/Test/Completion/AbstractCompletionProviderTests.cs @@ -430,8 +430,7 @@ protected virtual void SetWorkspaceOptions(TestWorkspace workspace) if (commitChar == '\t' || Controller.IsCommitCharacter(service.GetRules(), firstItem, commitChar, textTypedSoFar + commitChar)) { - var textChange = await DescriptionModifyingPresentationItem.GetTextChangeAsync( - service, document, firstItem, commitChar); + var textChange = (await service.GetChangeAsync(document, firstItem, commitChar, CancellationToken.None)).TextChange; // Adjust TextChange to include commit character, so long as it isn't TAB. if (commitChar != '\t') diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index f69fec4acfa5a6a3e4b9ab2e3dd1130f3634cd42..b664c087e078e1cbc9415c5c99398ce16b1f658e 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -648,7 +648,7 @@ class C state.SendTypeChars(" ") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem(displayText:="string", isHardSelected:=True) - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(c) c.Item.DisplayText = "int")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(c) c.DisplayText = "int")) End Using End Function @@ -673,9 +673,9 @@ class Foo state.SendTypeChars("a") Await state.AssertCompletionSession() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "num:")) - Assert.False(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "System")) - Assert.False(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(c) c.Item.DisplayText = "int")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "num:")) + Assert.False(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "System")) + Assert.False(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(c) c.DisplayText = "int")) End Using End Function @@ -698,7 +698,7 @@ class Foo state.SendTypeChars(", ") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem(displayText:="Numeros", isHardSelected:=True) - Assert.Equal(1, state.CurrentCompletionPresenterSession.PresentationItems.Where(Function(c) c.Item.DisplayText = "Numeros").Count()) + Assert.Equal(1, state.CurrentCompletionPresenterSession.CompletionItems.Where(Function(c) c.DisplayText = "Numeros").Count()) End Using End Function @@ -721,7 +721,7 @@ class Foo state.SendTypeChars(", ") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem(displayText:="Numeros", isHardSelected:=True) - Assert.Equal(1, state.CurrentCompletionPresenterSession.PresentationItems.Where(Function(c) c.Item.DisplayText = "Numeros").Count()) + Assert.Equal(1, state.CurrentCompletionPresenterSession.CompletionItems.Where(Function(c) c.DisplayText = "Numeros").Count()) End Using End Function @@ -791,13 +791,13 @@ class Program state.SendTypeChars("i") Await state.AssertCompletionSession() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "@int:")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "@int:")) state.SendTypeChars("n") Await state.WaitForAsynchronousOperationsAsync() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "@int:")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "@int:")) state.SendTypeChars("t") Await state.WaitForAsynchronousOperationsAsync() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "@int:")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "@int:")) End Using End Function @@ -1145,7 +1145,7 @@ class A ]]>) state.SendTypeChars("X") Await state.AssertCompletionSession() - Assert.False(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "X")) + Assert.False(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "X")) End Using End Function @@ -1503,7 +1503,7 @@ class AtAttribute : System.Attribute { }]]>) state.SendTypeChars("At") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem("At") - Assert.Equal("At", state.CurrentCompletionPresenterSession.SelectedItem.Item.FilterText) + Assert.Equal("At", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) End Using End Function @@ -1579,7 +1579,7 @@ class C state.SendTypeChars("Thing1") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem("Thing1") - Assert.True(state.CurrentCompletionPresenterSession.SelectedItem.Item.Tags.Contains(CompletionTags.Warning)) + Assert.True(state.CurrentCompletionPresenterSession.SelectedItem.Tags.Contains(CompletionTags.Warning)) state.SendBackspace() state.SendBackspace() state.SendBackspace() @@ -1589,7 +1589,7 @@ class C state.SendTypeChars("M") Await state.WaitForAsynchronousOperationsAsync() Await state.AssertSelectedCompletionItem("M") - Assert.False(state.CurrentCompletionPresenterSession.SelectedItem.Item.Tags.Contains(CompletionTags.Warning)) + Assert.False(state.CurrentCompletionPresenterSession.SelectedItem.Tags.Contains(CompletionTags.Warning)) End Using End Function diff --git a/src/EditorFeatures/Test2/IntelliSense/TestCompletionPresenterSession.vb b/src/EditorFeatures/Test2/IntelliSense/TestCompletionPresenterSession.vb index 6518e938d83a9b41b5a7e3e0670c1d97e37968f2..a4587157d98166884580206b55f6f094cc53440c 100644 --- a/src/EditorFeatures/Test2/IntelliSense/TestCompletionPresenterSession.vb +++ b/src/EditorFeatures/Test2/IntelliSense/TestCompletionPresenterSession.vb @@ -12,14 +12,14 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Private ReadOnly _testState As IIntelliSenseTestState Public TriggerSpan As ITrackingSpan - Public PresentationItems As IList(Of PresentationItem) - Public SelectedItem As PresentationItem + Public CompletionItems As IList(Of CompletionItem) + Public SelectedItem As CompletionItem Public IsSoftSelected As Boolean - Public SuggestionModeItem As PresentationItem + Public SuggestionModeItem As CompletionItem Public Event Dismissed As EventHandler(Of EventArgs) Implements ICompletionPresenterSession.Dismissed - Public Event ItemSelected As EventHandler(Of PresentationItemEventArgs) Implements ICompletionPresenterSession.ItemSelected - Public Event ItemCommitted As EventHandler(Of PresentationItemEventArgs) Implements ICompletionPresenterSession.ItemCommitted + Public Event ItemSelected As EventHandler(Of CompletionItemEventArgs) Implements ICompletionPresenterSession.ItemSelected + Public Event ItemCommitted As EventHandler(Of CompletionItemEventArgs) Implements ICompletionPresenterSession.ItemCommitted Public Event CompletionFiltersChanged As EventHandler(Of CompletionItemFilterStateChangedEventArgs) Implements ICompletionPresenterSession.FilterStateChanged Public Sub New(testState As IIntelliSenseTestState) @@ -27,16 +27,16 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense End Sub Public Sub PresentItems(triggerSpan As ITrackingSpan, - presentationItems As IList(Of PresentationItem), - selectedItem As PresentationItem, - suggestionModeItem As PresentationItem, + completionItems As IList(Of CompletionItem), + selectedItem As CompletionItem, + suggestionModeItem As CompletionItem, suggestionMode As Boolean, isSoftSelected As Boolean, completionItemFilters As ImmutableArray(Of CompletionItemFilter), filterText As String) Implements ICompletionPresenterSession.PresentItems _testState.CurrentCompletionPresenterSession = Me Me.TriggerSpan = triggerSpan - Me.PresentationItems = presentationItems + Me.CompletionItems = completionItems Me.SelectedItem = selectedItem Me.IsSoftSelected = isSoftSelected Me.SuggestionModeItem = suggestionModeItem @@ -46,21 +46,21 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense _testState.CurrentCompletionPresenterSession = Nothing End Sub - Public Sub SetSelectedItem(item As PresentationItem) + Public Sub SetSelectedItem(item As CompletionItem) Me.SelectedItem = item - RaiseEvent ItemSelected(Me, New PresentationItemEventArgs(item)) + RaiseEvent ItemSelected(Me, New CompletionItemEventArgs(item)) End Sub - Private Function GetFilteredItemAt(index As Integer) As PresentationItem - index = Math.Max(0, Math.Min(PresentationItems.Count - 1, index)) - Return PresentationItems(index) + Private Function GetFilteredItemAt(index As Integer) As CompletionItem + index = Math.Max(0, Math.Min(CompletionItems.Count - 1, index)) + Return CompletionItems(index) End Function Private Sub SelectPreviousItem() Implements ICompletionPresenterSession.SelectPreviousItem If IsSoftSelected Then IsSoftSelected = False Else - SetSelectedItem(GetFilteredItemAt(PresentationItems.IndexOf(SelectedItem) - 1)) + SetSelectedItem(GetFilteredItemAt(CompletionItems.IndexOf(SelectedItem) - 1)) End If End Sub @@ -68,18 +68,18 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense If IsSoftSelected Then IsSoftSelected = False Else - SetSelectedItem(GetFilteredItemAt(PresentationItems.IndexOf(SelectedItem) + 1)) + SetSelectedItem(GetFilteredItemAt(CompletionItems.IndexOf(SelectedItem) + 1)) End If End Sub Private Const s_itemsPerPage = 9 Public Sub SelectPreviousPageItem() Implements ICompletionPresenterSession.SelectPreviousPageItem - SetSelectedItem(GetFilteredItemAt(PresentationItems.IndexOf(SelectedItem) - s_itemsPerPage)) + SetSelectedItem(GetFilteredItemAt(CompletionItems.IndexOf(SelectedItem) - s_itemsPerPage)) End Sub Public Sub SelectNextPageItem() Implements ICompletionPresenterSession.SelectNextPageItem - SetSelectedItem(GetFilteredItemAt(PresentationItems.IndexOf(SelectedItem) + s_itemsPerPage)) + SetSelectedItem(GetFilteredItemAt(CompletionItems.IndexOf(SelectedItem) + s_itemsPerPage)) End Sub End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/TestState.vb b/src/EditorFeatures/Test2/IntelliSense/TestState.vb index e08bb2c66b7a8df740fefcf2b1ce0bac9d2b464a..2ea81e26ff6e560490e32cb15ac7c8d14b87fb7e 100644 --- a/src/EditorFeatures/Test2/IntelliSense/TestState.vb +++ b/src/EditorFeatures/Test2/IntelliSense/TestState.vb @@ -188,7 +188,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense MyBase.SendCommitUniqueCompletionListItem(Sub(a, n) handler.ExecuteCommand(a, n), Sub() Return) End Sub - Public Overloads Sub SendSelectCompletionItemThroughPresenterSession(item As PresentationItem) + Public Overloads Sub SendSelectCompletionItemThroughPresenterSession(item As CompletionItem) CurrentCompletionPresenterSession.SetSelectedItem(item) End Sub @@ -226,17 +226,18 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Function CompletionItemsContainsAll(displayText As String()) As Boolean AssertNoAsynchronousOperationsRunning() - Return displayText.All(Function(v) CurrentCompletionPresenterSession.PresentationItems.Any( - Function(i) i.Item.DisplayText = v)) + Return displayText.All(Function(v) CurrentCompletionPresenterSession.CompletionItems.Any( + Function(i) i.DisplayText = v)) End Function Public Function CompletionItemsContainsAny(displayText As String()) As Boolean AssertNoAsynchronousOperationsRunning() - Return displayText.Any(Function(v) CurrentCompletionPresenterSession.PresentationItems.Any( - Function(i) i.Item.DisplayText = v)) + Return displayText.Any(Function(v) CurrentCompletionPresenterSession.CompletionItems.Any( + Function(i) i.DisplayText = v)) End Function - Public Async Function AssertSelectedCompletionItem(Optional displayText As String = Nothing, + Public Async Function AssertSelectedCompletionItem( + Optional displayText As String = Nothing, Optional description As String = Nothing, Optional isSoftSelected As Boolean? = Nothing, Optional isHardSelected As Boolean? = Nothing, @@ -251,11 +252,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense End If If displayText IsNot Nothing Then - Assert.Equal(displayText, Me.CurrentCompletionPresenterSession.SelectedItem.Item.DisplayText) + Assert.Equal(displayText, Me.CurrentCompletionPresenterSession.SelectedItem.DisplayText) End If If shouldFormatOnCommit.HasValue Then - Assert.Equal(shouldFormatOnCommit.Value, Me.CurrentCompletionPresenterSession.SelectedItem.Item.Rules.FormatOnCommit) + Assert.Equal(shouldFormatOnCommit.Value, Me.CurrentCompletionPresenterSession.SelectedItem.Rules.FormatOnCommit) End If #If False Then @@ -266,7 +267,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense If description IsNot Nothing Then Dim document = Me.Workspace.CurrentSolution.Projects.First().Documents.First() - Dim itemDescription = Await Me.CurrentCompletionPresenterSession.SelectedItem.GetDescriptionAsync(document, CancellationToken.None) + Dim service = CompletionService.GetService(document) + Dim itemDescription = Await service.GetDescriptionAsync( + document, Me.CurrentCompletionPresenterSession.SelectedItem) Assert.Equal(description, itemDescription.Text) End If End Function diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 5b0c179b839f6a0d3af575848d0573d1cb80ce0a..2ec76a9be78f2fae3c1f21cea9f8fefcfe8b4ab0 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -345,7 +345,7 @@ End Class ) state.SendTypeChars(".A") Await state.AssertCompletionSession() - Assert.Equal(3, state.CurrentCompletionPresenterSession.PresentationItems.Count) + Assert.Equal(3, state.CurrentCompletionPresenterSession.CompletionItems.Count) End Using End Function @@ -371,10 +371,10 @@ End Class ) state.SendTypeChars(".A") Await state.WaitForAsynchronousOperationsAsync() - Assert.Equal(4, state.CurrentCompletionPresenterSession.PresentationItems.Count) + Assert.Equal(4, state.CurrentCompletionPresenterSession.CompletionItems.Count) state.SendTypeChars("A") Await state.WaitForAsynchronousOperationsAsync() - Assert.Equal(2, state.CurrentCompletionPresenterSession.PresentationItems.Count) + Assert.Equal(2, state.CurrentCompletionPresenterSession.CompletionItems.Count) End Using End Function @@ -565,8 +565,8 @@ End Class Await state.AssertCompletionSession() Await state.AssertSelectedCompletionItem(displayText:="A", isHardSelected:=True) Await state.WaitForAsynchronousOperationsAsync() - state.SendSelectCompletionItemThroughPresenterSession(state.CurrentCompletionPresenterSession.PresentationItems.First( - Function(i) i.Item.DisplayText = "B")) + state.SendSelectCompletionItemThroughPresenterSession(state.CurrentCompletionPresenterSession.CompletionItems.First( + Function(i) i.DisplayText = "B")) state.SendTab() Assert.Contains(".B", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) End Using @@ -1012,10 +1012,10 @@ End Class state.SendTypeChars("s") Await state.AssertCompletionSession() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "string:=")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "string:=")) state.SendTypeChars("t") Await state.WaitForAsynchronousOperationsAsync() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "string:=")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "string:=")) End Using End Function @@ -1039,8 +1039,8 @@ End Class state.SendTypeChars(" ") Await state.AssertCompletionSession() - Assert.Equal(1, state.CurrentCompletionPresenterSession.PresentationItems.Count) - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "str:=")) + Assert.Equal(1, state.CurrentCompletionPresenterSession.CompletionItems.Count) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "str:=")) End Using End Function @@ -1068,11 +1068,11 @@ End Class state.SendTypeChars(" ") Await state.AssertCompletionSession() - Assert.Equal(3, state.CurrentCompletionPresenterSession.PresentationItems.Count) - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "b:=")) - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "num:=")) - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "str:=")) - Assert.False(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = "dbl:=")) + Assert.Equal(3, state.CurrentCompletionPresenterSession.CompletionItems.Count) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "b:=")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "num:=")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "str:=")) + Assert.False(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = "dbl:=")) End Using End Function @@ -1615,10 +1615,10 @@ End Class ]]>) state.SendBackspace() Await state.AssertCompletionSession() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(c) c.Item.DisplayText = "AccessViolationException")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(c) c.DisplayText = "AccessViolationException")) state.SendBackspace() Await state.AssertCompletionSession() - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.Any(Function(c) c.Item.DisplayText = "AccessViolationException")) + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.Any(Function(c) c.DisplayText = "AccessViolationException")) End Using End Function @@ -1675,7 +1675,7 @@ End Class ]]>) state.SendTypeChars("selec") Await state.WaitForAsynchronousOperationsAsync() - Assert.Equal(state.CurrentCompletionPresenterSession.PresentationItems.Count, 2) + Assert.Equal(state.CurrentCompletionPresenterSession.CompletionItems.Count, 2) End Using End Function @@ -2049,9 +2049,9 @@ End Class) state.SendTypeChars("(") Await state.AssertCompletionSession() ' DayOfWeek.Monday should immediately follow DayOfWeek.Friday - Dim friday = state.CurrentCompletionPresenterSession.PresentationItems.First(Function(i) i.Item.DisplayText = "DayOfWeek.Friday") - Dim monday = state.CurrentCompletionPresenterSession.PresentationItems.First(Function(i) i.Item.DisplayText = "DayOfWeek.Monday") - Assert.True(state.CurrentCompletionPresenterSession.PresentationItems.IndexOf(friday) = state.CurrentCompletionPresenterSession.PresentationItems.IndexOf(monday) - 1) + Dim friday = state.CurrentCompletionPresenterSession.CompletionItems.First(Function(i) i.DisplayText = "DayOfWeek.Friday") + Dim monday = state.CurrentCompletionPresenterSession.CompletionItems.First(Function(i) i.DisplayText = "DayOfWeek.Monday") + Assert.True(state.CurrentCompletionPresenterSession.CompletionItems.IndexOf(friday) = state.CurrentCompletionPresenterSession.CompletionItems.IndexOf(monday) - 1) End Using End Function @@ -2140,8 +2140,8 @@ End Class state.SendInvokeCompletionList() Await state.WaitForAsynchronousOperationsAsync() ' Should only have one item called 'Double' and it should have a keyword glyph - Dim doubleItem = state.CurrentCompletionPresenterSession.PresentationItems.Single(Function(c) c.Item.DisplayText = "Double") - Assert.True(doubleItem.Item.Tags.Contains(CompletionTags.Keyword)) + Dim doubleItem = state.CurrentCompletionPresenterSession.CompletionItems.Single(Function(c) c.DisplayText = "Double") + Assert.True(doubleItem.Tags.Contains(CompletionTags.Keyword)) End Using End Function @@ -2160,10 +2160,10 @@ End Class state.SendInvokeCompletionList() Await state.WaitForAsynchronousOperationsAsync() ' We should have gotten the item corresponding to [Double] and the item for the Double keyword - Dim doubleItems = state.CurrentCompletionPresenterSession.PresentationItems.Where(Function(c) c.Item.DisplayText = "Double") + Dim doubleItems = state.CurrentCompletionPresenterSession.CompletionItems.Where(Function(c) c.DisplayText = "Double") Assert.Equal(2, doubleItems.Count()) - Assert.True(doubleItems.Any(Function(c) c.Item.Tags.Contains(CompletionTags.Keyword))) - Assert.True(doubleItems.Any(Function(c) c.Item.Tags.Contains(CompletionTags.Class) AndAlso c.Item.Tags.Contains(CompletionTags.Internal))) + Assert.True(doubleItems.Any(Function(c) c.Tags.Contains(CompletionTags.Keyword))) + Assert.True(doubleItems.Any(Function(c) c.Tags.Contains(CompletionTags.Class) AndAlso c.Tags.Contains(CompletionTags.Internal))) End Using End Function diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs index dc240b880c8de9bb252590de0a2ba34d7441ad9a..77cda7299defbad2b26be45098edcbf82c42b453 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs @@ -174,10 +174,8 @@ private bool IsAfterNameEqualsArgument(SyntaxToken token) rules: CompletionItemRules.Default); } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); private bool IsValid(ImmutableArray parameterList, ISet existingNamedParameters) { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs index a272f71e97d9576d4c561e672c2fd2263ffabf6a..4ba11bf50ab6563c37b307bd1f9cad80c4aaf307 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs @@ -312,10 +312,8 @@ private static TextSpan GetCompletionItemSpan(SourceText text, int position) rules: GetRules(insertionText)); } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); private static readonly CharacterSetModificationRule s_WithoutOpenBrace = CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '{'); private static readonly CharacterSetModificationRule s_WithoutOpenParen = CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '('); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs index 0f5342a9b2b5afc1c79a7a8244fc4c9479845658..6df94a6f6e53d80b8f61d7541c28bf2f147ebbba 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs @@ -127,10 +127,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) } } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); private static readonly CompletionItemRules s_rules = CompletionItemRules.Default.WithCommitCharacterRules(ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '.'))) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs index 9cedf6bc2f3535c3f6e17796ee7ef7a6caba44af..e3f6485843e1db26d8644029ea0e06384a6a3580 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceCompletionProvider.cs @@ -108,11 +108,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) } } - public override Task GetDescriptionAsync( - Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); public override Task GetTextChangeAsync( Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs index c20cc663c3621416741f5d4cff89bed172bd8705..5e53cfd429e62a09619461e15e691fbc3183ed8a 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs @@ -106,10 +106,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) } } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); private bool IsValid(ImmutableArray parameterList, ISet existingNamedParameters) { diff --git a/src/Features/Core/Portable/Completion/CommonCompletionProvider.cs b/src/Features/Core/Portable/Completion/CommonCompletionProvider.cs index f2f702c49a492e03804647838aeddc25ab4ecdfb..85c301e7c28e352ee052796f90eadb73655bd4dc 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionProvider.cs @@ -1,9 +1,13 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Snippets; using Microsoft.CodeAnalysis.Text; -using System.Threading; namespace Microsoft.CodeAnalysis.Completion { @@ -27,18 +31,55 @@ internal virtual bool IsInsertionTrigger(SourceText text, int insertedCharacterP return false; } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + public sealed override async Task GetDescriptionAsync( + Document document, CompletionItem item, CancellationToken cancellationToken) { - if (CommonCompletionItem.HasDescription(item)) - { - return Task.FromResult(CommonCompletionItem.GetDescription(item)); - } - else + // Get the actual description provided by whatever subclass we are. + // Then, if we would commit text that could be expanded as a snippet, + // put that information in the description so that the user knows. + var description = await this.GetDescriptionWorkerAsync(document, item, cancellationToken).ConfigureAwait(false); + var parts = await TryAddSnippetInvocationPart(document, item, description.TaggedParts, cancellationToken).ConfigureAwait(false); + + return description.WithTaggedParts(parts); + } + + private async Task> TryAddSnippetInvocationPart( + Document document, CompletionItem item, + ImmutableArray parts, CancellationToken cancellationToken) + { + var languageServices = document.Project.LanguageServices; + var snippetService = languageServices.GetService(); + if (snippetService != null) { - return Task.FromResult(CompletionDescription.Empty); + 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; } + protected virtual Task GetDescriptionWorkerAsync( + Document document, CompletionItem item, CancellationToken cancellationToken) + { + return CommonCompletionItem.HasDescription(item) + ? Task.FromResult(CommonCompletionItem.GetDescription(item)) + : Task.FromResult(CompletionDescription.Empty); + } + + public override async Task GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken)) { var change = (await GetTextChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false)) diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs index 87f3fcbaf1f46236de6731d8580ab997e437aad2..98c75f7cd1eadc522379de1233cd334c80850eef 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs @@ -253,9 +253,7 @@ internal virtual CompletionItemRules GetRules() return s_defaultRules; } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return MemberInsertionCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => MemberInsertionCompletionItem.GetDescriptionAsync(item, document, cancellationToken); } } \ No newline at end of file diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs index 6c5b6c560503b52d0da4dff63a1c0858d0ac60c0..2a84fecbca0d3a2b53fa90245798d1fcf853cd55 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs @@ -70,10 +70,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) } } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); protected abstract Task IsExclusiveAsync(Document document, int position, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs index 1c0cf668cbb29a6bc565ae2685f778405f002640..07397767a56d7d93335042337e0e286178f57758 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs @@ -110,10 +110,8 @@ private static bool NotNewDeclaredMember(INamedTypeSymbol symbol, SyntaxContext .Any(node => !(node.SyntaxTree == context.SyntaxTree && node.Span.IntersectsWith(context.Position))); } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); public override Task GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) { diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs index fde766e94b134c790ce02bf3038e7815318eb4ef..61cca6a0a848d5fbe64804ec3ff17586aa2a9e1b 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractSymbolCompletionProvider.cs @@ -132,10 +132,8 @@ protected AbstractSymbolCompletionProvider() return null; } - public override Task GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken) - { - return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); - } + protected override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken) + => SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken); protected virtual string GetFilterText(ISymbol symbol, string displayText, SyntaxContext context) { diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb index 1b63795548067cd55435cce0f7cdb186e5a2a404..f7e3269fa8322c8dcfdc79143f36504fc64a48b6 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb @@ -205,7 +205,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers rules:=GetRules(displayString)) End Function - Public Overrides Function GetDescriptionAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription) + Protected Overrides Function GetDescriptionWorkerAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription) If CommonCompletionItem.HasDescription(item) Then Return Task.FromResult(CommonCompletionItem.GetDescription(item)) Else diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb index b74047650ed4faf05946f1e69b7e11cda14a1a0a..b2032578794369ff0b06613ac3d5565974d49390 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb @@ -80,7 +80,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers WithFilterCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, ":"c, "="c)). WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Add, ":"c, "="c)) - Public Overrides Function GetDescriptionAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription) + Protected Overrides Function GetDescriptionWorkerAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription) Return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken) End Function diff --git a/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb b/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb index e1014a0b24dbb581bbe28bc88ed6c0d02ebf971e..5e0f0e38200c7b167d8880603fa35375e88345ae 100644 --- a/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb +++ b/src/VisualStudio/Core/Test/Completion/CSharpCompletionSnippetNoteTests.vb @@ -121,7 +121,9 @@ class C state.SendTypeChars("for") Await state.AssertCompletionSession() - Await state.AssertSelectedCompletionItem(description:=String.Format(FeaturesResources._0_Keyword, "for")) + Await state.AssertSelectedCompletionItem( + description:=String.Format(FeaturesResources._0_Keyword, "for") & vbCrLf & + String.Format(FeaturesResources.Note_colon_Tab_twice_to_insert_the_0_snippet, "for")) End Using End Function diff --git a/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb b/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb index 45d219ab7e09fa7a8df7cbf20e35d2a6d421ab69..239eeced6aa3f760bed24dd1eb2f5494f9c3aacd 100644 --- a/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb +++ b/src/VisualStudio/Core/Test/DebuggerIntelliSense/TestState.vb @@ -193,7 +193,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense MyBase.SendCommitUniqueCompletionListItem(Sub(a, n) handler.ExecuteCommand(a, n), Sub() Return) End Sub - Public Overloads Sub SendSelectCompletionItemThroughPresenterSession(item As PresentationItem) + Public Overloads Sub SendSelectCompletionItemThroughPresenterSession(item As CompletionItem) AssertNoAsynchronousOperationsRunning() CurrentCompletionPresenterSession.SetSelectedItem(item) End Sub @@ -219,7 +219,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense End If For Each displayText In displayTexts - If Not CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = displayText) Then + If Not CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = displayText) Then Assert.False(True, "Didn't find '" & displayText & "' in completion.") End If Next @@ -233,7 +233,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense End If For Each displayText In displayTexts - If CurrentCompletionPresenterSession.PresentationItems.Any(Function(i) i.Item.DisplayText = displayText) Then + If CurrentCompletionPresenterSession.CompletionItems.Any(Function(i) i.DisplayText = displayText) Then Assert.False(True, "Found '" & displayText & "' in completion.") End If Next @@ -256,7 +256,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense End If If displayText IsNot Nothing Then - Assert.Equal(displayText, Me.CurrentCompletionPresenterSession.SelectedItem.Item.DisplayText) + Assert.Equal(displayText, Me.CurrentCompletionPresenterSession.SelectedItem.DisplayText) End If #If False Then @@ -267,7 +267,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense If description IsNot Nothing Then Dim document = Me.Workspace.CurrentSolution.Projects.First().Documents.First() - Dim itemDescription = Await Me.CurrentCompletionPresenterSession.SelectedItem.GetDescriptionAsync(document, CancellationToken.None) + Dim service = CompletionService.GetService(document) + Dim itemDescription = Await service.GetDescriptionAsync(document, Me.CurrentCompletionPresenterSession.SelectedItem) Assert.Equal(description, itemDescription.Text) End If End Function diff --git a/src/VisualStudio/Core/Test/Snippets/SnippetCompletionProviderTests.vb b/src/VisualStudio/Core/Test/Snippets/SnippetCompletionProviderTests.vb index 1ba253e3cd4bf5eb615d8136ef875d81504722b0..cd97454a6aac6a071c881882c4cdea12cc15c477 100644 --- a/src/VisualStudio/Core/Test/Snippets/SnippetCompletionProviderTests.vb +++ b/src/VisualStudio/Core/Test/Snippets/SnippetCompletionProviderTests.vb @@ -23,12 +23,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets Assert.Equal(testState.GetDocumentText(), "a") Await testState.WaitForAsynchronousOperationsAsync() - Assert.Equal(testState.CurrentCompletionPresenterSession.SelectedItem.Item.DisplayText, "Shortcut") + Assert.Equal(testState.CurrentCompletionPresenterSession.SelectedItem.DisplayText, "Shortcut") Dim document = testState.Workspace.CurrentSolution.Projects.First().Documents.First() Dim service = document.Project.LanguageServices.GetService(Of CompletionService) - Dim itemDescription = Await service.GetDescriptionAsync(document, testState.CurrentCompletionPresenterSession.SelectedItem.Item) - Assert.Equal("Description", itemDescription.Text) + Dim itemDescription = Await service.GetDescriptionAsync(document, testState.CurrentCompletionPresenterSession.SelectedItem) + Assert.True(itemDescription.Text.StartsWith("Description")) testState.SendTabToCompletion() @@ -43,11 +43,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets Using testState testState.SendTabToCompletion() Await testState.WaitForAsynchronousOperationsAsync() - Assert.Equal(testState.CurrentCompletionPresenterSession.SelectedItem.Item.DisplayText, "Shortcut") + Assert.Equal(testState.CurrentCompletionPresenterSession.SelectedItem.DisplayText, "Shortcut") testState.SendBackspace() Await testState.WaitForAsynchronousOperationsAsync() - Assert.Equal(testState.CurrentCompletionPresenterSession.SelectedItem.Item.DisplayText, "Shortcut") + Assert.Equal(testState.CurrentCompletionPresenterSession.SelectedItem.DisplayText, "Shortcut") testState.SendTabToCompletion()