提交 0cce255f 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #14713 from CyrusNajmabadi/completionListModel

Simplifying the internal data model of Completion.
......@@ -199,13 +199,12 @@
<Compile Include="Extensibility\Commands\ExportInteractiveCommandAttribute.cs" />
<Compile Include="Extensibility\Commands\ICommandHandler.cs" />
<Compile Include="Extensibility\Commands\PredefinedCommandHandlerNames.cs" />
<Compile Include="Extensibility\Completion\PresentationItemEventArgs.cs" />
<Compile Include="Extensibility\Completion\CompletionItemEventArgs.cs" />
<Compile Include="Extensibility\Completion\CompletionItemFilterStateChangedEventArgs.cs" />
<Compile Include="Extensibility\Completion\ExportCompletionProviderAttribute.cs" />
<Compile Include="Extensibility\Completion\IAsyncCompletionService.cs" />
<Compile Include="Extensibility\Completion\ICompletionPresenterSession.cs" />
<Compile Include="Extensibility\Completion\ICustomCommitCompletionProvider.cs" />
<Compile Include="Extensibility\Completion\PresentationItem.cs" />
<Compile Include="Extensibility\Completion\PredefinedCompletionPresenterNames.cs" />
<Compile Include="Extensibility\Completion\PredefinedCompletionProviderNames.cs" />
<Compile Include="Extensibility\Completion\PredefinedCompletionRulesFactoryNames.cs" />
......@@ -268,7 +267,6 @@
<Compile Include="Implementation\Intellisense\Completion\Presentation\IVisualStudioCompletionSet.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\Roslyn14CompletionSet.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\VisualStudio14CompletionSet.cs" />
<Compile Include="Implementation\Intellisense\Completion\SimplePresentationItem.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\ICompletionSetFactory.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\ICompletionSet.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\VisualStudio14CompletionSetFactory.cs" />
......@@ -475,7 +473,6 @@
<Compile Include="Implementation\Intellisense\Completion\Controller_TabKey.cs" />
<Compile Include="Implementation\Intellisense\Completion\Controller_ToggleCompletionMode.cs" />
<Compile Include="Implementation\Intellisense\Completion\Controller_TypeChar.cs" />
<Compile Include="Implementation\Intellisense\Completion\DescriptionModifyingPresentationItem.cs" />
<Compile Include="Implementation\Intellisense\Completion\Model.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\CompletionPresenter.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\CompletionPresenterSession.cs" />
......@@ -783,4 +780,4 @@
<Folder Include="Extensibility\Navigation\" />
</ItemGroup>
<Import Project="..\..\..\build\Targets\VSL.Imports.targets" />
</Project>
</Project>
\ No newline at end of file
// 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 PresentationItemEventArgs : EventArgs
internal class CompletionItemEventArgs : EventArgs
{
public PresentationItem PresentationItem { get; }
public CompletionItem CompletionItem { get; }
public PresentationItemEventArgs(PresentationItem presentationItem)
public CompletionItemEventArgs(
CompletionItem completionItem)
{
this.PresentationItem = presentationItem;
CompletionItem = completionItem;
}
}
}
}
\ No newline at end of file
......@@ -11,8 +11,8 @@ namespace Microsoft.CodeAnalysis.Editor
internal interface ICompletionPresenterSession : IIntelliSensePresenterSession
{
void PresentItems(
ITrackingSpan triggerSpan, IList<PresentationItem> items, PresentationItem selectedItem,
PresentationItem suggestionModeItem, bool suggestionMode, bool isSoftSelected,
ITrackingSpan triggerSpan, IList<CompletionItem> items, CompletionItem selectedItem,
CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected,
ImmutableArray<CompletionItemFilter> completionItemFilters,
string filterText);
......@@ -21,8 +21,8 @@ internal interface ICompletionPresenterSession : IIntelliSensePresenterSession
void SelectPreviousPageItem();
void SelectNextPageItem();
event EventHandler<PresentationItemEventArgs> ItemSelected;
event EventHandler<PresentationItemEventArgs> ItemCommitted;
event EventHandler<CompletionItemEventArgs> ItemSelected;
event EventHandler<CompletionItemEventArgs> ItemCommitted;
event EventHandler<CompletionItemFilterStateChangedEventArgs> FilterStateChanged;
}
}
// 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<CompletionDescription> GetDescriptionAsync(Document document, CancellationToken cancellationToken);
}
}
\ No newline at end of file
......@@ -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(
......
......@@ -106,17 +106,13 @@ private async Task<Model> 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);
}
}
......
......@@ -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<T
}
var matchingCompletionItems = filterResults.Where(r => 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<T
var bestCompletionItem = GetBestCompletionItemBasedOnMRU(chosenItems, recentItems);
// If we don't have a best completion item yet, then pick the first item from the list.
var bestOrFirstCompletionItem = bestCompletionItem ?? filterResults.First().PresentationItem.Item;
var bestOrFirstPresentationItem = filterResults.Where(
r => 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<T
matchingItemCount == 1 &&
filterText.Length > 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> 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> 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<PresentationItem>.Empty)
return model.WithFilteredItems(ImmutableArray<CompletionItem>.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;
}
......
......@@ -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
......@@ -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<Model, PresentationItem> selector)
private void SetModelSelectedItem(Func<Model, CompletionItem> selector)
{
AssertIsForeground();
......@@ -29,7 +30,7 @@ public void SetModelIsHardSelection(bool isHardSelection)
private Model SetModelSelectedItemInBackground(
Model model,
Func<Model, PresentationItem> selector)
Func<Model, CompletionItem> 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<PresentationItem>(model.FilteredItems,
var filteredItemsSet = new HashSet<CompletionItem>(model.FilteredItems,
ReferenceEqualityComparer.Instance);
var newFilteredItems = model.TotalItems.Where(
......@@ -64,4 +65,4 @@ public void SetModelIsHardSelection(bool isHardSelection)
}
}
}
}
}
\ No newline at end of file
......@@ -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;
......
......@@ -151,16 +151,16 @@ internal bool AllFilterTextsEmpty(Model model, SnapshotPoint caretPoint)
private bool IsFilterTextEmpty(
Model model,
SnapshotPoint caretPoint,
PresentationItem item,
CompletionItem item,
Dictionary<TextSpan, string> textSpanToText,
Dictionary<TextSpan, ViewTextSpan> textSpanToViewSpan)
{
// Easy first check. See if the caret point is before the start of the item.
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;
......
......@@ -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<TextSpan, string> textSpanToText,
Dictionary<TextSpan, ViewTextSpan> 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
......@@ -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)
......
......@@ -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);
......
......@@ -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();
......
// 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);
}
/// <summary>
......@@ -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)
......
// 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<CompletionDescription> GetDescriptionAsync(Document document, CancellationToken cancellationToken)
{
var languageServices = document.Project.LanguageServices;
var snippetService = languageServices.GetService<ISnippetInfoService>();
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);
}
/// <summary>
/// Internal for testing purposes only.
/// </summary>
internal static async Task<TextChange> GetTextChangeAsync(
CompletionService service, Document document, CompletionItem item,
char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken))
{
var change = await service.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false);
// normally the items that produce multiple changes are not expecting to trigger the behaviors that rely on looking at the text
return change.TextChange;
}
}
}
\ No newline at end of file
......@@ -20,10 +20,9 @@ internal class Model
public Document TriggerDocument { get; }
public CompletionList OriginalList { get; }
public ImmutableArray<PresentationItem> TotalItems { get; }
public ImmutableArray<PresentationItem> FilteredItems { get; }
public ImmutableArray<CompletionItem> FilteredItems { get; }
public PresentationItem SelectedItem { get; }
public CompletionItem SelectedItem { get; }
public ImmutableArray<CompletionItemFilter> CompletionItemFilters { get; }
public ImmutableDictionary<CompletionItemFilter, bool> 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; }
/// <summary>
/// SuggestionMode item is the "builder" we would display in intellisense. It's
/// always non-null, but will only be shown if <see cref="UseSuggestionMode"/> is true.
/// If it provided by some <see cref="CompletionContext.SuggestionModeItem"/> then we
/// will use that. Otherwise, we'll have a simple empty-default item that we'll use.
/// </summary>
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<PresentationItem> totalItems,
ImmutableArray<PresentationItem> filteredItems,
PresentationItem selectedItem,
ImmutableArray<CompletionItem> filteredItems,
CompletionItem selectedItem,
ImmutableArray<CompletionItemFilter> completionItemFilters,
ImmutableDictionary<CompletionItemFilter, bool> 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<PresentationItem> 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<CompletionItemFilter, bool> 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<PresentationItem>.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<PresentationItem>();
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<CompletionItem> 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<ImmutableArray<PresentationItem>> filteredItems = default(Optional<ImmutableArray<PresentationItem>>),
Optional<PresentationItem> selectedItem = default(Optional<PresentationItem>),
Optional<ImmutableArray<CompletionItem>> filteredItems = default(Optional<ImmutableArray<CompletionItem>>),
Optional<CompletionItem> selectedItem = default(Optional<CompletionItem>),
Optional<ImmutableDictionary<CompletionItemFilter, bool>> filterState = default(Optional<ImmutableDictionary<CompletionItemFilter, bool>>),
Optional<string> filterText = default(Optional<string>),
Optional<bool> isHardSelection = default(Optional<bool>),
Optional<bool> isUnique = default(Optional<bool>),
Optional<bool> useSuggestionMode = default(Optional<bool>),
Optional<PresentationItem> suggestionModeItem = default(Optional<PresentationItem>),
Optional<CompletionItem> suggestionModeItem = default(Optional<CompletionItem>),
Optional<ITrackingPoint> commitTrackingSpanEndPoint = default(Optional<ITrackingPoint>))
{
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<PresentationItem> filteredItems)
public Model WithFilteredItems(ImmutableArray<CompletionItem> 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);
}
......
......@@ -21,11 +21,10 @@ internal sealed class CompletionPresenterSession : ForegroundThreadAffinitizedOb
internal readonly IGlyphService GlyphService;
private readonly ITextView _textView;
private readonly ITextBuffer _subjectBuffer;
public event EventHandler<EventArgs> Dismissed;
public event EventHandler<PresentationItemEventArgs> ItemCommitted;
public event EventHandler<PresentationItemEventArgs> ItemSelected;
public event EventHandler<CompletionItemEventArgs> ItemCommitted;
public event EventHandler<CompletionItemEventArgs> ItemSelected;
public event EventHandler<CompletionItemFilterStateChangedEventArgs> 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<PresentationItem> completionItems,
PresentationItem selectedItem,
PresentationItem suggestionModeItem,
IList<CompletionItem> completionItems,
CompletionItem selectedItem,
CompletionItem suggestionModeItem,
bool suggestionMode,
bool isSoftSelected,
ImmutableArray<CompletionItemFilter> 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<CompletionSelectionStatus> eventArgs)
private void OnCompletionSetSelectionStatusChanged(
object sender, ValueChangedEventArgs<CompletionSelectionStatus> 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));
}
}
......
// 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<CompletionDescription> 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<Document> GetDocumentAsync(CancellationToken cancellationToken)
......@@ -97,11 +89,11 @@ public override string IconAutomationText
}
}
public override System.Collections.Generic.IEnumerable<CompletionIcon> AttributeIcons
public override IEnumerable<CompletionIcon> 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) };
}
......
......@@ -16,13 +16,13 @@ internal interface ICompletionSet
CompletionSet CompletionSet { get; }
void SetCompletionItems(
IList<PresentationItem> completionItems,
PresentationItem selectedItem,
PresentationItem presetBuilder,
IList<CompletionItem> completionItems,
CompletionItem selectedItem,
CompletionItem suggestionModeItem,
bool suggestionMode,
bool isSoftSelected,
ImmutableArray<CompletionItemFilter> completionItemFilters,
string filterText);
PresentationItem GetPresentationItem(VSCompletion completion);
CompletionItem GetCompletionItem(VSCompletion completion);
}
}
......@@ -31,7 +31,9 @@ internal class Roslyn14CompletionSet : ForegroundThreadAffinitizedObject
protected readonly ITextBuffer SubjectBuffer;
protected readonly CompletionPresenterSession CompletionPresenterSession;
protected Dictionary<PresentationItem, VSCompletion> PresentationItemMap;
protected Dictionary<CompletionItem, VSCompletion> CompletionItemMap;
protected CompletionItem SuggestionModeItem;
protected string FilterText;
......@@ -57,9 +59,9 @@ public void SetTrackingSpan(ITrackingSpan trackingSpan)
}
public void SetCompletionItems(
IList<PresentationItem> completionItems,
PresentationItem selectedItem,
PresentationItem presetBuilder,
IList<CompletionItem> completionItems,
CompletionItem selectedItem,
CompletionItem suggestionModeItem,
bool suggestionMode,
bool isSoftSelected,
ImmutableArray<CompletionItemFilter> 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<PresentationItem, VSCompletion>(completionItems.Count + 1);
CompletionItemMap = CompletionItemMap ?? new Dictionary<CompletionItem, VSCompletion>(completionItems.Count + 1);
FilterText = filterText;
SuggestionModeItem = suggestionModeItem;
this.SetupFilters(completionItemFilters);
CreateCompletionListBuilder(selectedItem, suggestionModeItem, suggestionMode);
CreateNormalCompletionListItems(completionItems);
var selectedCompletionItem = GetVSCompletion(selectedItem);
VsCompletionSet.SelectionStatus = new CompletionSelectionStatus(
selectedCompletionItem,
isSelected: !isSoftSelected, isUnique: selectedCompletionItem != null);
}
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<CompletionItem> 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<CompletionItemFilter> 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)
{
......
......@@ -51,14 +51,18 @@ void ICompletionSet.SetTrackingSpan(ITrackingSpan trackingSpan)
_roslynCompletionSet.SetTrackingSpan(trackingSpan);
}
void ICompletionSet.SetCompletionItems(IList<PresentationItem> completionItems, PresentationItem selectedItem, PresentationItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> completionItemFilters, string filterText)
void ICompletionSet.SetCompletionItems(
IList<CompletionItem> completionItems, CompletionItem selectedItem,
CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> 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
......
// 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<CompletionDescription> GetDescriptionAsync(Document document, CancellationToken cancellationToken)
{
return this.CompletionService.GetDescriptionAsync(document, this.Item, cancellationToken);
}
}
}
......@@ -76,12 +76,12 @@ public IReadOnlyList<Span> 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();
......
......@@ -77,14 +77,14 @@ void ICompletionSet.SetTrackingSpan(ITrackingSpan trackingSpan)
_roslynCompletionSet.SetTrackingSpan(trackingSpan);
}
void ICompletionSet.SetCompletionItems(IList<PresentationItem> completionItems, PresentationItem selectedItem, PresentationItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> completionItemFilters, string filterText)
void ICompletionSet.SetCompletionItems(IList<CompletionItem> completionItems, CompletionItem selectedItem, CompletionItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> 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
......
......@@ -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')
......
......@@ -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
]]></Document>)
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 { }]]></Document>)
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
......
......@@ -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
......@@ -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
......
......@@ -345,7 +345,7 @@ End Class
</document>)
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
</document>)
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
]]></Document>)
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
]]></Document>)
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</Document>)
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
......
......@@ -174,10 +174,8 @@ private bool IsAfterNameEqualsArgument(SyntaxToken token)
rules: CompletionItemRules.Default);
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
private bool IsValid(ImmutableArray<IParameterSymbol> parameterList, ISet<string> existingNamedParameters)
{
......
......@@ -312,10 +312,8 @@ private static TextSpan GetCompletionItemSpan(SourceText text, int position)
rules: GetRules(insertionText));
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> 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, '(');
......
......@@ -127,10 +127,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
}
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> 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, '.')))
......
......@@ -108,11 +108,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
}
}
public override Task<CompletionDescription> GetDescriptionAsync(
Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
public override Task<TextChange?> GetTextChangeAsync(
Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
......
......@@ -106,10 +106,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
}
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
private bool IsValid(ImmutableArray<IParameterSymbol> parameterList, ISet<string> existingNamedParameters)
{
......
// 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<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
public sealed override async Task<CompletionDescription> 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<ImmutableArray<TaggedText>> TryAddSnippetInvocationPart(
Document document, CompletionItem item,
ImmutableArray<TaggedText> parts, CancellationToken cancellationToken)
{
var languageServices = document.Project.LanguageServices;
var snippetService = languageServices.GetService<ISnippetInfoService>();
if (snippetService != null)
{
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<CompletionDescription> GetDescriptionWorkerAsync(
Document document, CompletionItem item, CancellationToken cancellationToken)
{
return CommonCompletionItem.HasDescription(item)
? Task.FromResult(CommonCompletionItem.GetDescription(item))
: Task.FromResult(CompletionDescription.Empty);
}
public override async Task<CompletionChange> GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken))
{
var change = (await GetTextChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false))
......
......@@ -253,9 +253,7 @@ internal virtual CompletionItemRules GetRules()
return s_defaultRules;
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return MemberInsertionCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> MemberInsertionCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
}
\ No newline at end of file
......@@ -70,10 +70,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
}
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
protected abstract Task<bool> IsExclusiveAsync(Document document, int position, CancellationToken cancellationToken);
......
......@@ -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<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
public override Task<TextChange?> GetTextChangeAsync(Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken)
{
......
......@@ -132,10 +132,8 @@ protected AbstractSymbolCompletionProvider()
return null;
}
public override Task<CompletionDescription> GetDescriptionAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
{
return SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
}
protected override Task<CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
=> SymbolCompletionItem.GetDescriptionAsync(item, document, cancellationToken);
protected virtual string GetFilterText(ISymbol symbol, string displayText, SyntaxContext context)
{
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册