未验证 提交 f00691f5 编写于 作者: D dotnet-automerge-bot 提交者: GitHub

Merge pull request #37785 from dotnet/merges/release/dev16.3-preview2-vs-deps-to-master-vs-deps

Merge release/dev16.3-preview2-vs-deps to master-vs-deps
......@@ -8,7 +8,7 @@
<MajorVersion>3</MajorVersion>
<MinorVersion>3</MinorVersion>
<PatchVersion>0</PatchVersion>
<PreReleaseVersionLabel>beta3</PreReleaseVersionLabel>
<PreReleaseVersionLabel>beta2</PreReleaseVersionLabel>
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
<SemanticVersioningV1>true</SemanticVersioningV1>
<!--
......
......@@ -1280,7 +1280,7 @@ class C
var markup = CreateMarkupForSingleProject(file2, file1, LanguageNames.CSharp);
await VerifyTypeImportItemIsAbsentAsync(markup, "Bar", inlineDescription: "NS1");
}
private static void AssertRelativeOrder(List<string> expectedTypesInRelativeOrder, ImmutableArray<CompletionItem> allCompletionItems)
{
var hashset = new HashSet<string>(expectedTypesInRelativeOrder);
......
......@@ -701,6 +701,24 @@ internal class EditorFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to items from unimported namespaces.
/// </summary>
internal static string Expander_display_text {
get {
return ResourceManager.GetString("Expander_display_text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Expander.
/// </summary>
internal static string Expander_image_element {
get {
return ResourceManager.GetString("Expander_image_element", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Extract Interface.
/// </summary>
......@@ -1945,36 +1963,32 @@ internal class EditorFeaturesResources {
}
/// <summary>
/// Looks up a localized string similar to Toggling block comment....
/// Looks up a localized string similar to Toggle Line Comment.
/// </summary>
internal static string Toggling_block_comment {
internal static string Toggle_Line_Comment {
get {
return ResourceManager.GetString("Toggling_block_comment", resourceCulture);
return ResourceManager.GetString("Toggle_Line_Comment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Toggle Line Comment.
/// Looks up a localized string similar to Toggling block comment....
/// </summary>
internal static string Toggle_Line_Comment
{
get
{
return ResourceManager.GetString("Toggle_Line_Comment", resourceCulture);
internal static string Toggling_block_comment {
get {
return ResourceManager.GetString("Toggling_block_comment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Toggling line comment....
/// </summary>
internal static string Toggling_line_comment
{
get
{
internal static string Toggling_line_comment {
get {
return ResourceManager.GetString("Toggling_line_comment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Type Parts.
/// </summary>
......
......@@ -917,4 +917,10 @@ Do you want to proceed?</value>
<data name="Toggling_line_comment" xml:space="preserve">
<value>Toggling line comment...</value>
</data>
<data name="Expander_display_text" xml:space="preserve">
<value>items from unimported namespaces</value>
</data>
<data name="Expander_image_element" xml:space="preserve">
<value>Expander</value>
</data>
</root>
......@@ -264,7 +264,7 @@ private void AddBlockComment(SnapshotSpan span, ArrayBuilder<TextChange> textCha
}
private void UncommentPosition(CommentSelectionInfo info, SnapshotSpan span, ArrayBuilder<TextChange> textChanges,
ArrayBuilder<CommentTrackingSpan> spansToSelect,int positionOfStart, int positionOfEnd)
ArrayBuilder<CommentTrackingSpan> spansToSelect, int positionOfStart, int positionOfEnd)
{
if (positionOfStart < 0 || positionOfEnd < 0)
{
......
......@@ -124,11 +124,10 @@ internal CommitManager(ITextView textView, RecentItemsManager recentItemsManager
return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None);
}
if (!session.Properties.TryGetProperty(CompletionSource.TriggerSnapshot, out ITextSnapshot triggerSnapshot))
if (!Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation))
{
// Need the trigger snapshot to calculate the span when the commit changes to be applied.
// It should be inserted into a property bag within GetCompletionContextAsync for each item created by Roslyn.
// If not found here, Roslyn should not make a commit.
// They should always be available from VS. Just to be defensive, if it's not found here, Roslyn should not make a commit.
return CommitResultUnhandled;
}
......@@ -137,7 +136,7 @@ internal CommitManager(ITextView textView, RecentItemsManager recentItemsManager
return CommitResultUnhandled;
}
var triggerDocument = triggerSnapshot.GetOpenDocumentInCurrentContextWithChanges();
var triggerDocument = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
if (triggerDocument == null)
{
return CommitResultUnhandled;
......@@ -148,7 +147,7 @@ internal CommitManager(ITextView textView, RecentItemsManager recentItemsManager
{
AsyncCompletionLogger.LogCommitWithTypeImportCompletionEnabled();
if (roslynItem.IsCached)
if (roslynItem.Flags.IsCached())
{
AsyncCompletionLogger.LogCommitOfTypeImportCompletionItem();
}
......@@ -169,7 +168,7 @@ internal CommitManager(ITextView textView, RecentItemsManager recentItemsManager
var commitChar = typeChar == '\0' ? null : (char?)typeChar;
var commitBehavior = Commit(
triggerDocument, completionService, session.TextView, subjectBuffer,
roslynItem, completionListSpan, commitChar, triggerSnapshot, serviceRules,
roslynItem, completionListSpan, commitChar, triggerLocation.Snapshot, serviceRules,
filterText, cancellationToken);
_recentItemsManager.MakeMostRecentItem(roslynItem.FilterText);
......
......@@ -2,7 +2,8 @@
using Microsoft.CodeAnalysis.Completion;
using Microsoft.VisualStudio.Text;
using AsyncCompletionData = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
using EditorAsyncCompletion = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
using EditorAsyncCompletionData = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
using RoslynTrigger = Microsoft.CodeAnalysis.Completion.CompletionTrigger;
using RoslynCompletionItem = Microsoft.CodeAnalysis.Completion.CompletionItem;
using VSCompletionItem = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem;
......@@ -21,7 +22,7 @@ internal static class Helpers
/// We retrieve this character from triggerLocation.
/// </param>
/// <returns>Roslyn completion trigger</returns>
internal static RoslynTrigger GetRoslynTrigger(AsyncCompletionData.CompletionTrigger trigger, SnapshotPoint triggerLocation)
internal static RoslynTrigger GetRoslynTrigger(EditorAsyncCompletionData.CompletionTrigger trigger, SnapshotPoint triggerLocation)
{
var completionTriggerKind = GetRoslynTriggerKind(trigger);
if (completionTriggerKind == CompletionTriggerKind.Deletion)
......@@ -46,32 +47,32 @@ internal static RoslynTrigger GetRoslynTrigger(AsyncCompletionData.CompletionTri
}
}
internal static CompletionTriggerKind GetRoslynTriggerKind(AsyncCompletionData.CompletionTrigger trigger)
internal static CompletionTriggerKind GetRoslynTriggerKind(EditorAsyncCompletionData.CompletionTrigger trigger)
{
switch (trigger.Reason)
{
case AsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique:
case EditorAsyncCompletionData.CompletionTriggerReason.InvokeAndCommitIfUnique:
return CompletionTriggerKind.InvokeAndCommitIfUnique;
case AsyncCompletionData.CompletionTriggerReason.Insertion:
case EditorAsyncCompletionData.CompletionTriggerReason.Insertion:
return CompletionTriggerKind.Insertion;
case AsyncCompletionData.CompletionTriggerReason.Deletion:
case AsyncCompletionData.CompletionTriggerReason.Backspace:
case EditorAsyncCompletionData.CompletionTriggerReason.Deletion:
case EditorAsyncCompletionData.CompletionTriggerReason.Backspace:
return CompletionTriggerKind.Deletion;
case AsyncCompletionData.CompletionTriggerReason.SnippetsMode:
case EditorAsyncCompletionData.CompletionTriggerReason.SnippetsMode:
return CompletionTriggerKind.Snippets;
default:
return CompletionTriggerKind.Invoke;
}
}
internal static CompletionFilterReason GetFilterReason(AsyncCompletionData.CompletionTrigger trigger)
internal static CompletionFilterReason GetFilterReason(EditorAsyncCompletionData.CompletionTrigger trigger)
{
switch (trigger.Reason)
{
case AsyncCompletionData.CompletionTriggerReason.Insertion:
case EditorAsyncCompletionData.CompletionTriggerReason.Insertion:
return CompletionFilterReason.Insertion;
case AsyncCompletionData.CompletionTriggerReason.Deletion:
case AsyncCompletionData.CompletionTriggerReason.Backspace:
case EditorAsyncCompletionData.CompletionTriggerReason.Deletion:
case EditorAsyncCompletionData.CompletionTriggerReason.Backspace:
return CompletionFilterReason.Deletion;
default:
return CompletionFilterReason.Other;
......@@ -113,6 +114,18 @@ internal static bool IsFilterCharacter(RoslynCompletionItem item, char ch, strin
return false;
}
internal static bool TryGetInitialTriggerLocation(EditorAsyncCompletion.IAsyncCompletionSession session, out SnapshotPoint initialTriggerLocation)
{
if (session is EditorAsyncCompletion.IAsyncCompletionSessionOperations sessionOperations)
{
initialTriggerLocation = sessionOperations.InitialTriggerLocation;
return true;
}
initialTriggerLocation = default;
return false;
}
// This is a temporarily method to support preference of IntelliCode items comparing to non-IntelliCode items.
// We expect that Editor will intorduce this support and we will get rid of relying on the "★" then.
internal static bool IsPreferredItem(this RoslynCompletionItem completionItem)
......
......@@ -9,8 +9,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
......@@ -114,16 +112,23 @@ internal ItemManager(RecentItemsManager recentItemsManager)
}
}
// We need to filter if a non-empty strict subset of filters are selected
var selectedFilters = data.SelectedFilters.Where(f => f.IsSelected).Select(f => f.Filter).ToImmutableArray();
var needToFilter = selectedFilters.Length > 0 && selectedFilters.Length < data.SelectedFilters.Length;
// We need to filter if
// 1. a non-empty strict subset of filters are selected
// 2. a non-empty set of expanders are unselected
var nonExpanderFilterStates = data.SelectedFilters.WhereAsArray(f => !(f.Filter is CompletionExpander));
var selectedNonExpanderFilters = nonExpanderFilterStates.Where(f => f.IsSelected).SelectAsArray(f => f.Filter);
var needToFilter = selectedNonExpanderFilters.Length > 0 && selectedNonExpanderFilters.Length < nonExpanderFilterStates.Length;
var unselectedExpanders = data.SelectedFilters.Where(f => !f.IsSelected && f.Filter is CompletionExpander).SelectAsArray(f => f.Filter);
var needToFilterExpanded = unselectedExpanders.Length > 0;
if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isExperimentEnabled) && isExperimentEnabled)
{
// Telemetry: Want to know % of sessions with the "Target type matches" filter where that filter is actually enabled
if (needToFilter &&
!session.Properties.ContainsProperty(_targetTypeCompletionFilterChosenMarker) &&
selectedFilters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))
selectedNonExpanderFilters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))
{
AsyncCompletionLogger.LogTargetTypeFilterChosenInSession();
......@@ -136,10 +141,9 @@ internal ItemManager(RecentItemsManager recentItemsManager)
// If the session was created/maintained out of Roslyn, e.g. in debugger; no properties are set and we should use data.Snapshot.
// However, we prefer using the original snapshot in some projection scenarios.
if (!session.Properties.TryGetProperty(CompletionSource.TriggerSnapshot, out ITextSnapshot snapshotForDocument))
{
snapshotForDocument = data.Snapshot;
}
var snapshotForDocument = Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation)
? triggerLocation.Snapshot
: data.Snapshot;
var document = snapshotForDocument.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext();
var completionService = document?.GetLanguageService<CompletionService>();
......@@ -151,7 +155,12 @@ internal ItemManager(RecentItemsManager recentItemsManager)
{
cancellationToken.ThrowIfCancellationRequested();
if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedFilters))
if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedNonExpanderFilters))
{
continue;
}
if (needToFilterExpanded && ShouldBeFilteredOutOfExpandedCompletionList(item, unselectedExpanders))
{
continue;
}
......@@ -242,6 +251,39 @@ internal ItemManager(RecentItemsManager recentItemsManager)
highlightedList,
completionHelper,
hasSuggestedItemOptions);
static bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item, ImmutableArray<CompletionFilter> activeNonExpanderFilters)
{
if (item.Filters.Any(filter => activeNonExpanderFilters.Contains(filter)))
{
return false;
}
return true;
}
static bool ShouldBeFilteredOutOfExpandedCompletionList(VSCompletionItem item, ImmutableArray<CompletionFilter> unselectedExpanders)
{
var associatedWithUnselectedExpander = false;
foreach (var itemFilter in item.Filters)
{
if (itemFilter is CompletionExpander)
{
if (!unselectedExpanders.Contains(itemFilter))
{
// If any of the associated expander is selected, the item should be included in the expanded list.
return false;
}
associatedWithUnselectedExpander = true;
}
}
// at this point, the item either:
// 1. has no expander filter, therefore should be included
// 2. or, all associated expanders are unselected, therefore should be excluded
return associatedWithUnselectedExpander;
}
}
private static bool IsAfterDot(ITextSnapshot snapshot, ITrackingSpan applicableToSpan)
......@@ -451,21 +493,9 @@ private static bool IsAfterDot(ITextSnapshot snapshot, ITrackingSpan applicableT
// See which filters might be enabled based on the typed code
var textFilteredFilters = filteredList.SelectMany(n => n.VSCompletionItem.Filters).ToImmutableHashSet();
// When no items are available for a given filter, it becomes unavailable
return ImmutableArray.CreateRange(filters.Select(n => n.WithAvailability(textFilteredFilters.Contains(n.Filter))));
}
private static bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item, ImmutableArray<CompletionFilter> activeFilters)
{
foreach (var itemFilter in item.Filters)
{
if (activeFilters.Contains(itemFilter))
{
return false;
}
}
return true;
// When no items are available for a given filter, it becomes unavailable.
// Expanders always appear available as long as it's presented.
return filters.SelectAsArray(n => n.WithAvailability(n.Filter is CompletionExpander ? true : textFilteredFilters.Contains(n.Filter)));
}
/// <summary>
......
......@@ -15,6 +15,9 @@ internal static class GlyphExtensions
// https://github.com/dotnet/roslyn/issues/26642
private static readonly Guid ImageCatalogGuid = Guid.Parse("ae27a6b0-e345-4288-96df-5eaf394ee369");
public static ImageId GetImageCatalogImageId(int imageId)
=> new ImageId(ImageCatalogGuid, imageId);
public static ImageId GetImageId(this Glyph glyph)
{
// VS for mac cannot refer to ImageMoniker
......
......@@ -37,6 +37,16 @@
<target state="translated">Chcete přesto pokračovat? Výsledkem může být poškozený kód.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">Při extrahování metody došlo k následujícím problémům:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Möchten Sie dennoch fortfahren? Möglicherweise wird fehlerhafter Code generiert.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">Beim Extrahieren der Methode sind folgende Fehler aufgetreten:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">¿Desea continuar? Esto puede producir código roto.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">El método de extracción detectó los siguientes problemas:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Voulez-vous continuer ? Le code sera interrompu.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">La méthode Extract a rencontré les problèmes suivants :</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Procedere comunque? Il codice ottenuto potrebbe essere danneggiato.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">Il metodo di estrazione ha rilevato i problemi seguenti:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">続行しますか。破損状態のコードが生成される可能性があります。</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">メソッドの抽出で次の問題が発生しました:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">계속하시겠습니까? 계속하면 손상된 코드가 생성될 수 있습니다.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">메서드 추출에서 다음 문제가 발생했습니다.</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Czy nadal chcesz kontynuować? Może to spowodować uszkodzenie kodu.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">Metoda ekstrakcji napotkała następujące problemy:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Ainda quer continuar? Isso pode produzir código desfeito.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">O método de extração encontrou os seguintes problemas:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Вы действительно хотите продолжить? Это может привести к появлению нерабочего кода.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">При работе метода извлечения возникли следующие ошибки:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">Yine de devam etmek istiyor musunuz? Bu işlem sonucunda bozuk kod oluşturulabilir.</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">Ayıklama metodu aşağıdaki sorunlarla karşılaştı:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">是否仍要继续? 这可能会导致代码损坏。</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">Extract 方法遇到以下问题:</target>
......
......@@ -37,6 +37,16 @@
<target state="translated">仍要繼續嗎? 這可能產生中斷的程式碼。</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="new">items from unimported namespaces</target>
<note />
</trans-unit>
<trans-unit id="Expander_image_element">
<source>Expander</source>
<target state="new">Expander</target>
<note />
</trans-unit>
<trans-unit id="Extract_method_encountered_the_following_issues">
<source>Extract method encountered the following issues:</source>
<target state="translated">擷取方法發生下列問題:</target>
......
......@@ -5289,18 +5289,244 @@ namespace NS2
public class Bar { }
}
"
' Enable import completion and disable timebox.
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1) ' disable timebox for import completion
state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="Bar", isHardSelected:=True, inlineDescription:="NS2")
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestExpanderWithImportCompletionDisabled() As Task
Using state = TestStateFactory.CreateCSharpTestState(CompletionImplementation.Modern,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
}
namespace NS2
{
public class Bar { }
}
]]></Document>)
state.Workspace.Options = state.Workspace.Options.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, False)
' trigger completion with import completion disabled
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemsDoNotContainAny(displayText:={"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' select expander
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
' unselect expander
state.SetCompletionItemExpanderState(isSelected:=False)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemsDoNotContainAny(displayText:={"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' select expander again
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestExpanderWithImportCompletionEnabled() As Task
Using state = TestStateFactory.CreateCSharpTestState(CompletionImplementation.Modern,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
}
namespace NS2
{
public class Bar { }
}
]]></Document>)
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1) ' disable timebox for import completion
' trigger completion with import completion enabled
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
' unselect expander
state.SetCompletionItemExpanderState(isSelected:=False)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemsDoNotContainAny(displayText:={"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' select expander
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
' unselect expander again
state.SetCompletionItemExpanderState(isSelected:=False)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemsDoNotContainAny(displayText:={"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestExpanderAndTimeboxWithImportCompletionEnabled() As Task
Using state = TestStateFactory.CreateCSharpTestState(CompletionImplementation.Modern,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
}
namespace NS2
{
public class Bar { }
}
]]></Document>)
' Enable import completion and set timeout to 0 (so always timeout)
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, 0)
' trigger completion with import completion enabled, this should timeout so no unimport types should be shown and expander should be unselected
' (but the caculation should continue in background)
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemsDoNotContainAny(displayText:={"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' select expander
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
' timeout is ignored if user asked for unimport types explicitly (via expander)
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestExpanderAndTimeboxWithImportCompletionDisabled() As Task
Using state = TestStateFactory.CreateCSharpTestState(CompletionImplementation.Modern,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
}
namespace NS2
{
public class Bar { }
}
]]></Document>)
' Disable import completion
state.Workspace.Options = state.Workspace.Options.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, False)
' trigger completion with import completion disabled
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemsDoNotContainAny(displayText:={"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' set timeout to 0 (always timeout)
state.Workspace.Options = state.Workspace.Options.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, 0)
' select expander
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
' timeout should be ignored since user asked for unimport types explicitly (via expander)
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function NoExpanderAvailableWhenNotInTypeContext() As Task
Using state = TestStateFactory.CreateCSharpTestState(CompletionImplementation.Modern,
<Document><![CDATA[
namespace NS1
{
$$
}
]]></Document>)
state.Workspace.Options = state.Workspace.Options.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
' trigger completion with import completion enabled
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemExpander(isAvailable:=False, isSelected:=False)
End Using
End Function
<WorkItem(34943, "https://github.com/dotnet/roslyn/issues/34943")>
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion), Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)>
......@@ -5594,6 +5820,198 @@ class C
End Using
End Function
<WorkItem(35163, "https://github.com/dotnet/roslyn/issues/35163")>
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function NonExpandedItemShouldAlwaysBePreferred_DisplayTextMatch(completionImplementation As CompletionImplementation) As Task
Using state = TestStateFactory.CreateCSharpTestState(completionImplementation,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
public class Bar<T>
{
}
}
namespace NS2
{
public class Bar
{
}
}
]]></Document>)
Dim expectedText = "
namespace NS1
{
class C
{
public void Foo()
{
Bar
}
}
public class Bar<T>
{
}
}
namespace NS2
{
public class Bar
{
}
}
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="Bar", displayTextSuffix:="<>")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
End Function
<WorkItem(35163, "https://github.com/dotnet/roslyn/issues/35163")>
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function NonExpandedItemShouldAlwaysBePreferred_FullDisplayTextMatch(completionImplementation As CompletionImplementation) As Task
Using state = TestStateFactory.CreateCSharpTestState(completionImplementation,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
public class Bar
{
}
}
namespace NS2
{
public class Bar
{
}
}
]]></Document>)
Dim expectedText = "
namespace NS1
{
class C
{
public void Foo()
{
Bar
}
}
public class Bar
{
}
}
namespace NS2
{
public class Bar
{
}
}
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="Bar")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
End Function
<WorkItem(35163, "https://github.com/dotnet/roslyn/issues/35163")>
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function NonExpandedItemShouldAlwaysBePreferred_ExpandedItemHasBetterMatch(completionImplementation As CompletionImplementation) As Task
Using state = TestStateFactory.CreateCSharpTestState(completionImplementation,
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
bar$$
}
}
public class BitArrayReceiver
{
}
}
namespace NS2
{
public class Bar
{
}
}
]]></Document>)
Dim expectedText = "
namespace NS1
{
class C
{
public void Foo()
{
BitArrayReceiver
}
}
public class BitArrayReceiver
{
}
}
namespace NS2
{
public class Bar
{
}
}
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="BitArrayReceiver")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
End Function
Private Class MultipleChangeCompletionProvider
Inherits CompletionProvider
......
......@@ -58,7 +58,7 @@ class c
End Function
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory(Skip := "https://github.com/dotnet/roslyn/issues/35631"), Trait(Traits.Feature, Traits.Features.Completion)>
<WpfTheory(Skip:="https://github.com/dotnet/roslyn/issues/35631"), Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestCaretPlacement(completionImplementation As CompletionImplementation) As Task
Using state = TestStateFactory.CreateCSharpTestState(completionImplementation,
<Document><![CDATA[
......
......@@ -34,7 +34,7 @@ end class
End Function
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory(Skip := "https://github.com/dotnet/roslyn/issues/35631"), Trait(Traits.Feature, Traits.Features.Completion)>
<WpfTheory(Skip:="https://github.com/dotnet/roslyn/issues/35631"), Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestCaretPlacement(completionImplementation As CompletionImplementation) As Task
Using state = TestStateFactory.CreateVisualBasicTestState(completionImplementation,
<Document><![CDATA[
......
......@@ -130,6 +130,14 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Return CurrentCompletionPresenterSession.CompletionItemFilters
End Function
Public Overrides Sub AssertCompletionItemExpander(isAvailable As Boolean, isSelected As Boolean)
Throw New NotImplementedException()
End Sub
Public Overrides Sub SetCompletionItemExpanderState(isSelected As Boolean)
Throw New NotImplementedException()
End Sub
Public Overrides Function HasSuggestedItem() As Boolean
' SuggestionModeItem is always not null but is displayed only when SuggestionMode = True
Return CurrentCompletionPresenterSession.SuggestionMode
......
......@@ -62,7 +62,16 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
End Sub
Public Function GetFilters() As ImmutableArray(Of CompletionFilterWithState)
Return _filters
Return _filters.WhereAsArray(Function(state) TypeOf state.Filter IsNot CompletionExpander)
End Function
Public Sub SetExpander(isSelected As Boolean)
_filters = _filters.Select(Function(n) If(TypeOf n.Filter Is CompletionExpander, n.WithSelected(isSelected), n)).ToImmutableArray()
RaiseEvent FiltersChanged(Me, New CompletionFilterChangedEventArgs(_filters))
End Sub
Public Function GetExpander() As CompletionFilterWithState
Return _filters.SingleOrDefault(Function(state) TypeOf state.Filter Is CompletionExpander)
End Function
Public Sub TriggerFiltersChanged(sender As Object, args As CompletionFilterChangedEventArgs)
......
......@@ -319,6 +319,25 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Return presenter.GetFilters().Select(Function(f) New CompletionItemFilter(f.Filter.DisplayText, "", f.Filter.AccessKey(0))).ToImmutableArray()
End Function
Public Overrides Sub AssertCompletionItemExpander(isAvailable As Boolean, isSelected As Boolean)
Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(Me.TextView), MockCompletionPresenter)
Dim expander = presenter.GetExpander()
If Not isAvailable Then
Assert.False(isSelected)
Assert.Null(expander)
Else
Assert.NotNull(expander)
Assert.Equal(expander.IsSelected, isSelected)
End If
End Sub
Public Overrides Sub SetCompletionItemExpanderState(isSelected As Boolean)
Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(Me.TextView), MockCompletionPresenter)
Dim expander = presenter.GetExpander()
Assert.NotNull(expander)
presenter.SetExpander(isSelected)
End Sub
Public Overrides Function HasSuggestedItem() As Boolean
AssertNoAsynchronousOperationsRunning()
Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
......
......@@ -195,6 +195,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Public MustOverride Function GetCompletionItemFilters() As ImmutableArray(Of CompletionItemFilter)
Public MustOverride Sub AssertCompletionItemExpander(isAvailable As Boolean, isSelected As Boolean)
Public MustOverride Sub SetCompletionItemExpanderState(isSelected As Boolean)
Public MustOverride Function HasSuggestedItem() As Boolean
Public MustOverride Function IsSoftSelected() As Boolean
......
// 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.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Tags;
namespace Microsoft.CodeAnalysis.Completion
......@@ -32,6 +35,17 @@ protected override CompletionItem GetBetterItem(CompletionItem item, CompletionI
return base.GetBetterItem(item, existingItem);
}
internal override Task<(CompletionList completionList, bool expandItemsAvailable)> GetCompletionsInternalAsync(
Document document,
int caretPosition,
CompletionTrigger trigger,
ImmutableHashSet<string> roles,
OptionSet options,
CancellationToken cancellationToken)
{
return GetCompletionsWithAvailabilityOfExpandedItemsAsync(document, caretPosition, trigger, roles, options, cancellationToken);
}
protected static bool IsKeywordItem(CompletionItem item)
{
return item.Tags.Contains(WellKnownTags.Keyword);
......
......@@ -70,6 +70,14 @@ public sealed class CompletionContext
/// </summary>
public bool IsExclusive { get; set; }
/// <summary>
/// Set to true if the corresponding provider can provide extended items with currect context,
/// regardless of whether those items are actually added. i.e. it might be disabled by default,
/// but we still want to show the expander so user can explicitly request them to be added to
/// completion list if we are in the aproperiate context.
/// </summary>
internal bool ExpandItemsAvailable { get; set; }
/// <summary>
/// Creates a <see cref="CompletionContext"/> instance.
/// </summary>
......
......@@ -188,7 +188,16 @@ private static bool IsKeywordItem(CompletionItem item)
private int CompareMatches(PatternMatch match1, PatternMatch match2, CompletionItem item1, CompletionItem item2)
{
// First see how the two items compare in a case insensitive fashion. Matches that
// Always prefer non-expanded item regardless of the pattern matching result.
// This currently means unimported types will be treated as "2nd tier" results,
// which forces users to be more explicit about selecting them.
var expandedDiff = CompareExpandedItem(item1, item2);
if (expandedDiff != 0)
{
return expandedDiff;
}
// Then see how the two items compare in a case insensitive fashion. Matches that
// are strictly better (ignoring case) should prioritize the item. i.e. if we have
// a prefix match, that should always be better than a substring match.
//
......@@ -252,5 +261,19 @@ private int ComparePreselection(CompletionItem item1, CompletionItem item2)
return 0;
}
private int CompareExpandedItem(CompletionItem item1, CompletionItem item2)
{
var isItem1Expanded = item1.Flags.IsExpanded();
var isItem2Expanded = item2.Flags.IsExpanded();
if (isItem1Expanded == isItem2Expanded)
{
return 0;
}
// Non-expanded item is better than expanded item
return isItem1Expanded ? 1 : -1;
}
}
}
......@@ -90,15 +90,7 @@ public sealed class CompletionItem : IComparable<CompletionItem>
/// </summary>
internal string AutomationText { get; set; }
/// <summary>
/// Indicate whether this <see cref="CompletionItem"/> is cached and reused across completion sessions.
/// This might be used by completion system for things like deciding whether it can safaly cache and reuse
/// other data correspodning to this item.
///
/// TODO: Revisit the approach we used for caching VS items.
/// https://github.com/dotnet/roslyn/issues/35160
/// </summary>
internal bool IsCached { get; set; }
internal CompletionItemFlags Flags { get; set; }
private CompletionItem(
string displayText,
......@@ -259,7 +251,8 @@ public sealed class CompletionItem : IComparable<CompletionItem>
inlineDescription: newInlineDescription)
{
AutomationText = AutomationText,
ProviderName = ProviderName
ProviderName = ProviderName,
Flags = Flags,
};
}
......
// 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.Completion
{
[Flags]
internal enum CompletionItemFlags
{
None = 0x0,
/// <summary>
/// Indicates this <see cref="CompletionItem"/> is cached and reused across completion sessions.
/// This might be used by completion system for things like deciding whether it can safaly cache and reuse
/// other data correspodning to this item.
///
/// TODO: Revisit the approach we used for caching VS items.
/// https://github.com/dotnet/roslyn/issues/35160
/// </summary>
Cached = 0x1,
/// <summary>
/// Indicates this <see cref="CompletionItem"/> should be shown only when expanded items is requested.
/// </summary>
Expanded = 0x2,
CachedAndExpanded = Cached | Expanded,
}
internal static class CompletionItemFlagsExtensions
{
public static bool IsCached(this CompletionItemFlags flags)
=> (flags & CompletionItemFlags.Cached) != 0;
public static bool IsExpanded(this CompletionItemFlags flags)
=> (flags & CompletionItemFlags.Expanded) != 0;
}
}
......@@ -64,5 +64,10 @@ internal virtual Task<CompletionChange> GetChangeAsync(Document document, Comple
/// True if the provider produces snippet items.
/// </summary>
internal virtual bool IsSnippetProvider => false;
/// <summary>
/// True if the provider produces items show be shown in expanded list only.
/// </summary>
internal virtual bool IsExpandItemProvider => false;
}
}
......@@ -91,6 +91,25 @@ public virtual TextSpan GetDefaultCompletionListSpan(SourceText text, int caretP
OptionSet options = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets the completions available at the caret position, with additional info indicates
/// whether expander items are available.
/// </summary>
/// <remarks>
/// expandItemsAvailable is true when expanded items are returned or can be provided upon request.
/// </remarks>
internal virtual async Task<(CompletionList completionList, bool expandItemsAvailable)> GetCompletionsInternalAsync(
Document document,
int caretPosition,
CompletionTrigger trigger = default,
ImmutableHashSet<string> roles = null,
OptionSet options = null,
CancellationToken cancellationToken = default)
{
var completionList = await GetCompletionsAsync(document, caretPosition, trigger, roles, options, cancellationToken).ConfigureAwait(false);
return (completionList, false);
}
/// <summary>
/// Gets the description of the item.
/// </summary>
......
......@@ -6,6 +6,12 @@ namespace Microsoft.CodeAnalysis.Completion
{
internal static class CompletionServiceOptions
{
/// <summary>
/// Indicates if the completion is trigger by toggle the expander.
/// </summary>
public static readonly Option<bool> IsExpandedCompletion
= new Option<bool>(nameof(CompletionServiceOptions), nameof(IsExpandedCompletion), defaultValue: false);
/// <summary>
/// Timeout value used for time-boxing import completion.
/// Telemetry shows that the average processing time with cache warmed up for 99th percentile is ~700ms,
......
......@@ -152,6 +152,11 @@ protected ImmutableArray<CompletionProvider> GetProviders(ImmutableHashSet<strin
CompletionTrigger trigger,
OptionSet options)
{
if (options.GetOption(CompletionServiceOptions.IsExpandedCompletion))
{
providers = providers.WhereAsArray(p => p.IsExpandItemProvider);
}
// If the caller passed along specific options that affect snippets,
// then defer to those. Otherwise if the caller just wants the default
// behavior, then get the snippets behavior from our own rules.
......@@ -212,6 +217,18 @@ private CompletionProvider GetProviderByName(string providerName)
ImmutableHashSet<string> roles,
OptionSet options,
CancellationToken cancellationToken)
{
var (completionList, _) = await GetCompletionsWithAvailabilityOfExpandedItemsAsync(document, caretPosition, trigger, roles, options, cancellationToken).ConfigureAwait(false);
return completionList;
}
private protected async Task<(CompletionList completionList, bool expandItemsAvailable)> GetCompletionsWithAvailabilityOfExpandedItemsAsync(
Document document,
int caretPosition,
CompletionTrigger trigger,
ImmutableHashSet<string> roles,
OptionSet options,
CancellationToken cancellationToken)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var defaultItemSpan = GetDefaultCompletionListSpan(text, caretPosition);
......@@ -242,7 +259,7 @@ private CompletionProvider GetProviderByName(string providerName)
// Now, ask all the triggered providers, in parallel, to populate a completion context.
// Note: we keep any context with items *or* with a suggested item.
var triggeredCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
var (triggeredCompletionContexts, expandItemsAvailableFromTriggeredProviders) = await ComputeNonEmptyCompletionContextsAsync(
document, caretPosition, trigger, options,
defaultItemSpan, triggeredProviders,
cancellationToken).ConfigureAwait(false);
......@@ -252,7 +269,7 @@ private CompletionProvider GetProviderByName(string providerName)
// want to show any completion.
if (!triggeredCompletionContexts.Any(cc => cc.Items.Count > 0))
{
return null;
return (null, expandItemsAvailableFromTriggeredProviders);
}
// All the contexts should be non-empty or have a suggestion item.
......@@ -264,10 +281,8 @@ private CompletionProvider GetProviderByName(string providerName)
if (exclusiveContexts.Any())
{
return MergeAndPruneCompletionLists(
exclusiveContexts,
defaultItemSpan,
isExclusive: true);
return (MergeAndPruneCompletionLists(exclusiveContexts, defaultItemSpan, isExclusive: true),
expandItemsAvailableFromTriggeredProviders);
}
// Shouldn't be any exclusive completion contexts at this point.
......@@ -279,7 +294,7 @@ private CompletionProvider GetProviderByName(string providerName)
// we'll want to augment the list with all the regular symbol completion items.
var augmentingProviders = providers.Except(triggeredProviders).ToImmutableArray();
var augmentingCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
var (augmentingCompletionContexts, expandItemsAvailableFromAugmentingProviders) = await ComputeNonEmptyCompletionContextsAsync(
document, caretPosition, trigger, options, defaultItemSpan,
augmentingProviders, cancellationToken).ConfigureAwait(false);
......@@ -290,7 +305,8 @@ private CompletionProvider GetProviderByName(string providerName)
// groups are properly ordered based on the original providers.
allContexts = allContexts.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
return MergeAndPruneCompletionLists(allContexts, defaultItemSpan, isExclusive: false);
return (MergeAndPruneCompletionLists(allContexts, defaultItemSpan, isExclusive: false),
(expandItemsAvailableFromTriggeredProviders || expandItemsAvailableFromAugmentingProviders));
}
private static bool HasAnyItems(CompletionContext cc)
......@@ -298,7 +314,7 @@ private static bool HasAnyItems(CompletionContext cc)
return cc.Items.Count > 0 || cc.SuggestionModeItem != null;
}
private async Task<ImmutableArray<CompletionContext>> ComputeNonEmptyCompletionContextsAsync(
private async Task<(ImmutableArray<CompletionContext>, bool)> ComputeNonEmptyCompletionContextsAsync(
Document document, int caretPosition, CompletionTrigger trigger,
OptionSet options, TextSpan defaultItemSpan,
ImmutableArray<CompletionProvider> providers,
......@@ -314,7 +330,8 @@ private static bool HasAnyItems(CompletionContext cc)
var completionContexts = await Task.WhenAll(completionContextTasks).ConfigureAwait(false);
var nonEmptyContexts = completionContexts.Where(HasAnyItems).ToImmutableArray();
return nonEmptyContexts;
var shouldShowExpander = completionContexts.Any(context => context.ExpandItemsAvailable);
return (nonEmptyContexts, shouldShowExpander);
}
private CompletionList MergeAndPruneCompletionLists(
......
......@@ -10,7 +10,6 @@
using Microsoft.CodeAnalysis.AddImports;
using Microsoft.CodeAnalysis.Completion.Log;
using Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Experiments;
......@@ -38,31 +37,42 @@ internal abstract partial class AbstractTypeImportCompletionProvider : CommonCom
protected abstract Task<bool> IsInImportsDirectiveAsync(Document document, int position, CancellationToken cancellationToken);
internal override bool IsExpandItemProvider => true;
public override async Task ProvideCompletionsAsync(CompletionContext completionContext)
{
var cancellationToken = completionContext.CancellationToken;
var document = completionContext.Document;
var workspace = document.Project.Solution.Workspace;
var importCompletionOptionValue = completionContext.Options.GetOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, document.Project.Language);
// Don't trigger import completion if the option value is "default" and the experiment is disabled for the user.
if (importCompletionOptionValue == false ||
(importCompletionOptionValue == null && !IsTypeImportCompletionExperimentEnabled(workspace)))
// We need to check for context before option values, so we can tell completion service that we are in a context to provide expanded items
// even though import completion might be disabled. This would show the expander in completion list which user can then use to explicitly ask for unimported items.
var syntaxContext = await CreateContextAsync(document, completionContext.Position, cancellationToken).ConfigureAwait(false);
if (!syntaxContext.IsTypeContext)
{
return;
}
var syntaxContext = await CreateContextAsync(document, completionContext.Position, cancellationToken).ConfigureAwait(false);
if (!syntaxContext.IsTypeContext)
completionContext.ExpandItemsAvailable = true;
// We will trigger import completion regardless of the option/experiment if extended items is being requested explicitly (via expander in completion list)
var isExpandedCompletion = completionContext.Options.GetOption(CompletionServiceOptions.IsExpandedCompletion);
if (!isExpandedCompletion)
{
return;
var importCompletionOptionValue = completionContext.Options.GetOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, document.Project.Language);
// Don't trigger import completion if the option value is "default" and the experiment is disabled for the user.
if (importCompletionOptionValue == false ||
(importCompletionOptionValue == null && !IsTypeImportCompletionExperimentEnabled(workspace)))
{
return;
}
}
using (Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken))
using (var telemetryCounter = new TelemetryCounter())
{
await AddCompletionItemsAsync(completionContext, syntaxContext, telemetryCounter, cancellationToken).ConfigureAwait(false);
await AddCompletionItemsAsync(completionContext, syntaxContext, isExpandedCompletion, telemetryCounter, cancellationToken).ConfigureAwait(false);
}
}
......@@ -77,7 +87,7 @@ private bool IsTypeImportCompletionExperimentEnabled(Workspace workspace)
return _isTypeImportCompletionExperimentEnabled == true;
}
private async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, TelemetryCounter telemetryCounter, CancellationToken cancellationToken)
private async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, bool isExpandedCompletion, TelemetryCounter telemetryCounter, CancellationToken cancellationToken)
{
var document = completionContext.Document;
var project = document.Project;
......@@ -105,15 +115,17 @@ private async Task AddCompletionItemsAsync(CompletionContext completionContext,
tasksToGetCompletionItems.AddRange(
referencedAssemblySymbols.Select(symbol => Task.Run(() => HandleReferenceAsync(symbol))));
// We want to timebox the operation that might need to traverse all the type symbols and populate the cache,
// the idea is not to block completion for too long (likely to happen the first time import completion is triggered).
// We want to timebox the operation that might need to traverse all the type symbols and populate the cache.
// The idea is not to block completion for too long (likely to happen the first time import completion is triggered).
// The trade-off is we might not provide unimported types until the cache is warmed up.
var timeoutInMilliseconds = completionContext.Options.GetOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion);
var combinedTask = Task.WhenAll(tasksToGetCompletionItems.ToImmutableAndFree());
if (timeoutInMilliseconds != 0 && await Task.WhenAny(combinedTask, Task.Delay(timeoutInMilliseconds, cancellationToken)).ConfigureAwait(false) == combinedTask)
if (isExpandedCompletion ||
timeoutInMilliseconds != 0 && await Task.WhenAny(combinedTask, Task.Delay(timeoutInMilliseconds, cancellationToken)).ConfigureAwait(false) == combinedTask)
{
// No timeout. We now have all completion items ready.
// Either there's no timeout, and we now have all completion items ready,
// or user asked for unimported type explicitly so we need to wait until they are calculated.
var completionItemsToAdd = await combinedTask.ConfigureAwait(false);
foreach (var completionItems in completionItemsToAdd)
{
......
......@@ -271,10 +271,7 @@ public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool is
var isAttribute = symbol.Name.HasAttributeSuffix(isCaseSensitive: false) && symbol.IsAttribute();
var item = TypeImportCompletionItem.Create(symbol, containingNamespace, _genericTypeSuffix);
item.IsCached = true;
_itemsBuilder.Add(new TypeImportCompletionItemInfo(item, isPublic, isGeneric, isAttribute));
return;
}
}
......
......@@ -34,8 +34,8 @@ public static CompletionItem Create(INamedTypeSymbol typeSymbol, string containi
// it also makes sure type with shorter name shows first, e.g. 'SomeType` before 'SomeTypeWithLongerName'.
var sortTextBuilder = PooledStringBuilder.GetInstance();
sortTextBuilder.Builder.AppendFormat(SortTextFormat, typeSymbol.Name, containingNamespace);
return CompletionItem.Create(
var item = CompletionItem.Create(
displayText: typeSymbol.Name,
filterText: typeSymbol.Name,
sortText: sortTextBuilder.ToStringAndFree(),
......@@ -45,6 +45,9 @@ public static CompletionItem Create(INamedTypeSymbol typeSymbol, string containi
displayTextPrefix: null,
displayTextSuffix: typeSymbol.Arity == 0 ? string.Empty : genericTypeSuffix,
inlineDescription: containingNamespace);
item.Flags = CompletionItemFlags.CachedAndExpanded;
return item;
}
public static CompletionItem CreateAttributeItemWithoutSuffix(CompletionItem attributeItem, string attributeNameWithoutSuffix)
......
......@@ -80,7 +80,7 @@
<ProjectReference Include="..\..\..\EditorFeatures\Text\Microsoft.CodeAnalysis.EditorFeatures.Text.csproj" />
<ProjectReference Include="..\..\..\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj" />
<ProjectReference Include="..\..\..\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj" />
<ProjectReference Include="..\..\..\Interactive\Host\Microsoft.CodeAnalysis.InteractiveHost.csproj" Aliases="InteractiveHost" />
<ProjectReference Include="..\..\..\Interactive\Host\Microsoft.CodeAnalysis.InteractiveHost.csproj" PrivateAssets="all" Aliases="InteractiveHost" />
</ItemGroup>
<ItemGroup Label="File References">
<Reference Include="System.ComponentModel.Composition" />
......
......@@ -2,6 +2,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.IntegrationTest.Utilities;
......@@ -22,6 +23,14 @@ public CSharpIntelliSense(VisualStudioInstanceFactory instanceFactory, ITestOutp
{
}
public override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(true);
// Disable import completion.
VisualStudio.Workspace.SetImportCompletionOption(false);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
public void AtNamespaceLevel()
{
......
......@@ -16,7 +16,7 @@ public CSharpInteractiveCommands(VisualStudioInstanceFactory instanceFactory, IT
{
}
[WpfFact]
[WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/18779")]
public void VerifyPreviousAndNextHistory()
{
VisualStudio.InteractiveWindow.SubmitText("1 + 2");
......
// 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.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.IntegrationTest.Utilities;
......@@ -20,6 +21,14 @@ public BasicIntelliSense(VisualStudioInstanceFactory instanceFactory, ITestOutpu
{
}
public override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(true);
// Disable import completion.
VisualStudio.Workspace.SetImportCompletionOption(false);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
public void IntelliSenseTriggersOnParenWithBraceCompletionAndCorrectUndoMerging()
{
......
......@@ -55,6 +55,21 @@ public void CleanUpWaitingService()
public void SetQuickInfo(bool value)
=> _inProc.EnableQuickInfo(value);
public void SetImportCompletionOption(bool value)
{
SetPerLanguageOption(
optionName: "ShowItemsFromUnimportedNamespaces",
feature: "CompletionOptions",
language: LanguageNames.CSharp,
value: value);
SetPerLanguageOption(
optionName: "ShowItemsFromUnimportedNamespaces",
feature: "CompletionOptions",
language: LanguageNames.VisualBasic,
value: value);
}
public void SetFullSolutionAnalysis(bool value)
{
SetPerLanguageOption(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册