diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs index 7c3c35951a8c2..527c8e8308225 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs @@ -26,16 +26,20 @@ public class ExtensionMethodImportCompletionProviderTests : AbstractCSharpComple private bool? ShowImportCompletionItemsOptionValue { get; set; } = true; + // -1 would disable timebox, whereas 0 means always timeout. + private int TimeoutInMilliseconds { get; set; } = -1; + private bool IsExpandedCompletion { get; set; } = true; - private bool HideAdvancedMembers { get; set; } = false; + private bool HideAdvancedMembers { get; set; } protected override OptionSet WithChangedOptions(OptionSet options) { return options .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, ShowImportCompletionItemsOptionValue) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) - .WithChangedOption(CompletionOptions.HideAdvancedMembers, LanguageNames.CSharp, HideAdvancedMembers); + .WithChangedOption(CompletionOptions.HideAdvancedMembers, LanguageNames.CSharp, HideAdvancedMembers) + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, TimeoutInMilliseconds); } protected override TestComposition GetComposition() @@ -1884,6 +1888,44 @@ await VerifyImportItemIsAbsentAsync( } } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task TestTimeBox() + { + var file1 = @" +using System; + +namespace Foo +{ + public static class ExtensionClass + { + public static bool ExtentionMethod(this int x) + => true; + } +}"; + var file2 = @" +using System; + +namespace Baz +{ + public class Bat + { + public void M(int x) + { + x.$$ + } + } +}"; + + IsExpandedCompletion = false; + TimeoutInMilliseconds = 0; //timeout immediately + var markup = GetMarkup(file2, file1, ReferenceType.None); + + await VerifyImportItemIsAbsentAsync( + markup, + "ExtentionMethod", + inlineDescription: "Foo"); + } + private Task VerifyImportItemExistsAsync(string markup, string expectedItem, int glyph, string inlineDescription, string displayTextSuffix = null, string expectedDescriptionOrNull = null) => VerifyItemExistsAsync(markup, expectedItem, displayTextSuffix: displayTextSuffix, glyph: glyph, inlineDescription: inlineDescription, expectedDescriptionOrNull: expectedDescriptionOrNull); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs index 1d270ff66864f..6a70c80fdaf67 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs @@ -13,7 +13,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -82,6 +81,14 @@ public Task UpdateCompletionListAsync( CancellationToken cancellationToken) => Task.FromResult(UpdateCompletionList(session, data, cancellationToken)); + // We might need to handle large amount of items with import completion enabled, + // so use a dedicated pool to minimize/avoid array allocations (especially in LOH) + // Set the size of pool to 1 because we don't expect UpdateCompletionListAsync to be + // called concurrently, which essentially makes the pooled list a singleton, + // but we still use ObjectPool for concurrency handling just to be robust. + private static readonly ObjectPool> s_listOfMatchResultPool + = new(factory: () => new(), size: 1); + private FilteredCompletionModel UpdateCompletionList( IAsyncCompletionSession session, AsyncCompletionSessionDataSnapshot data, @@ -176,81 +183,89 @@ private FilteredCompletionModel UpdateCompletionList( // Use a monotonically increasing integer to keep track the original alphabetical order of each item. var currentIndex = 0; - var builder = ArrayBuilder.GetInstance(); - foreach (var item in data.InitialSortedList) + var initialListOfItemsToBeIncluded = s_listOfMatchResultPool.Allocate(); + try { - cancellationToken.ThrowIfCancellationRequested(); - - if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedNonExpanderFilters)) + // Filter items based on the selected filters and matching. + foreach (var item in data.InitialSortedList) { - continue; - } + cancellationToken.ThrowIfCancellationRequested(); - if (needToFilterExpanded && ShouldBeFilteredOutOfExpandedCompletionList(item, unselectedExpanders)) - { - continue; + if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedNonExpanderFilters)) + { + continue; + } + + if (needToFilterExpanded && ShouldBeFilteredOutOfExpandedCompletionList(item, unselectedExpanders)) + { + continue; + } + + if (TryCreateMatchResult( + completionHelper, + item, + filterText, + initialRoslynTriggerKind, + filterReason, + _recentItemsManager.RecentItems, + highlightMatchingPortions: highlightMatchingPortions, + ref currentIndex, + out var matchResult)) + { + initialListOfItemsToBeIncluded.Add(matchResult); + } } - if (TryCreateMatchResult( - completionHelper, - item, - filterText, - initialRoslynTriggerKind, - filterReason, - _recentItemsManager.RecentItems, - highlightMatchingPortions: highlightMatchingPortions, - ref currentIndex, - out var matchResult)) + if (initialListOfItemsToBeIncluded.Count == 0) { - builder.Add(matchResult); + return HandleAllItemsFilteredOut(reason, data.SelectedFilters, completionRules); } - } - - if (builder.Count == 0) - { - return HandleAllItemsFilteredOut(reason, data.SelectedFilters, completionRules); - } - // Sort the items by pattern matching results. - // Note that we want to preserve the original alphabetical order for items with same pattern match score, - // but `ArrayBuilder.Sort` isn't stable. Therefore we have to add a monotonically increasing integer - // to `MatchResult` to achieve this. - builder.Sort(MatchResult.SortingComparer); + // Sort the items by pattern matching results. + // Note that we want to preserve the original alphabetical order for items with same pattern match score, + // but `List.Sort` isn't stable. Therefore we have to add a monotonically increasing integer + // to `MatchResult` to achieve this. + initialListOfItemsToBeIncluded.Sort(MatchResult.SortingComparer); - var initialListOfItemsToBeIncluded = builder.ToImmutableAndFree(); + var showCompletionItemFilters = options?.GetOption(CompletionOptions.ShowCompletionItemFilters, document.Project.Language) ?? true; - var showCompletionItemFilters = options?.GetOption(CompletionOptions.ShowCompletionItemFilters, document.Project.Language) ?? true; + var updatedFilters = showCompletionItemFilters + ? GetUpdatedFilters(initialListOfItemsToBeIncluded, data.SelectedFilters) + : ImmutableArray.Empty; - var updatedFilters = showCompletionItemFilters - ? GetUpdatedFilters(initialListOfItemsToBeIncluded, data.SelectedFilters) - : ImmutableArray.Empty; + // If this was deletion, then we control the entire behavior of deletion ourselves. + if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion) + { + return HandleDeletionTrigger(data.Trigger.Reason, initialListOfItemsToBeIncluded, filterText, updatedFilters); + } - // If this was deletion, then we control the entire behavior of deletion ourselves. - if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion) - { - return HandleDeletionTrigger(data.Trigger.Reason, initialListOfItemsToBeIncluded, filterText, updatedFilters); - } + Func, string, ImmutableArray> filterMethod; + if (completionService == null) + { + filterMethod = (itemsWithPatternMatches, text) => CompletionService.FilterItems(completionHelper, itemsWithPatternMatches); + } + else + { + filterMethod = (itemsWithPatternMatches, text) => completionService.FilterItems(document, itemsWithPatternMatches, text); + } - Func, string, ImmutableArray> filterMethod; - if (completionService == null) - { - filterMethod = (itemsWithPatternMatches, text) => CompletionService.FilterItems(completionHelper, itemsWithPatternMatches); + return HandleNormalFiltering( + filterMethod, + filterText, + updatedFilters, + filterReason, + data.Trigger.Character, + initialListOfItemsToBeIncluded, + hasSuggestedItemOptions); } - else + finally { - filterMethod = (itemsWithPatternMatches, text) => completionService.FilterItems(document, itemsWithPatternMatches, text); + // Don't call ClearAndFree, which resets the capacity to a default value. + initialListOfItemsToBeIncluded.Clear(); + s_listOfMatchResultPool.Free(initialListOfItemsToBeIncluded); } - return HandleNormalFiltering( - filterMethod, - filterText, - updatedFilters, - filterReason, - data.Trigger.Character, - initialListOfItemsToBeIncluded, - hasSuggestedItemOptions); - static bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item, ImmutableArray activeNonExpanderFilters) { if (item.Filters.Any(filter => activeNonExpanderFilters.Contains(filter))) @@ -297,7 +312,7 @@ private FilteredCompletionModel HandleNormalFiltering( ImmutableArray filters, CompletionFilterReason filterReason, char typeChar, - ImmutableArray itemsInList, + List itemsInList, bool hasSuggestedItemOptions) { // Not deletion. Defer to the language to decide which item it thinks best @@ -321,7 +336,7 @@ private FilteredCompletionModel HandleNormalFiltering( var longestCommonPrefixLength = bestOrFirstMatchResult.RoslynCompletionItem.FilterText.GetCaseInsensitivePrefixLength(filterText); - for (var i = 1; i < itemsInList.Length; ++i) + for (var i = 1; i < itemsInList.Count; ++i) { var item = itemsInList[i]; var commonPrefixLength = item.RoslynCompletionItem.FilterText.GetCaseInsensitivePrefixLength(filterText); @@ -383,11 +398,11 @@ private FilteredCompletionModel HandleNormalFiltering( private static FilteredCompletionModel HandleDeletionTrigger( CompletionTriggerReason filterTriggerKind, - ImmutableArray matchResults, + List matchResults, string filterText, ImmutableArray filters) { - var matchingItems = matchResults.WhereAsArray(r => r.MatchedFilterText); + var matchingItems = matchResults.Where(r => r.MatchedFilterText); if (filterTriggerKind == CompletionTriggerReason.Insertion && !matchingItems.Any()) { @@ -455,7 +470,7 @@ private static FilteredCompletionModel HandleDeletionTrigger( uniqueItem: moreThanOneMatchWithSamePriority ? null : bestMatchResult.GetValueOrDefault().VSCompletionItem); } - private static ImmutableArray GetHighlightedList(ImmutableArray matchResults) + private static ImmutableArray GetHighlightedList(List matchResults) => matchResults.SelectAsArray(matchResult => new CompletionItemWithHighlight(matchResult.VSCompletionItem, matchResult.HighlightedSpans)); @@ -490,11 +505,11 @@ private static FilteredCompletionModel HandleAllItemsFilteredOut( } private static ImmutableArray GetUpdatedFilters( - ImmutableArray filteredList, + List filteredList, ImmutableArray filters) { // See which filters might be enabled based on the typed code - var textFilteredFilters = filteredList.SelectMany(n => n.VSCompletionItem.Filters).ToImmutableHashSet(); + var textFilteredFilters = filteredList.SelectMany(n => n.VSCompletionItem.Filters).ToHashSet(); // When no items are available for a given filter, it becomes unavailable. // Expanders always appear available as long as it's presented. @@ -570,6 +585,21 @@ private static RoslynCompletionItem GetOrAddRoslynCompletionItem(VSCompletionIte return roslynItem; } + // If the item didn't match the filter text, we still keep it in the list + // if one of two things is true: + // 1. The user has typed nothing or only typed a single character. In this case they might + // have just typed the character to get completion. Filtering out items + // here is not desirable. + // + // 2. They brought up completion with ctrl-j or through deletion. In these + // cases we just always keep all the items in the list. + private static bool KeepAllItemsInTheList(CompletionTriggerKind initialTriggerKind, string filterText) + { + return filterText.Length <= 1 || + initialTriggerKind == CompletionTriggerKind.Invoke || + initialTriggerKind == CompletionTriggerKind.Deletion; + } + private static bool TryCreateMatchResult( CompletionHelper completionHelper, VSCompletionItem item, @@ -601,23 +631,11 @@ private static bool TryCreateMatchResult( recentItems, patternMatch); - // If the item didn't match the filter text, we still keep it in the list - // if one of two things is true: - // - // 1. The user has typed nothing or only typed a single character. In this case they might - // have just typed the character to get completion. Filtering out items - // here is not desirable. - // - // 2. They brought up completion with ctrl-j or through deletion. In these - // cases we just always keep all the items in the list. - if (matchedFilterText || - initialTriggerKind == CompletionTriggerKind.Deletion || - initialTriggerKind == CompletionTriggerKind.Invoke || - filterText.Length <= 1) + if (matchedFilterText || KeepAllItemsInTheList(initialTriggerKind, filterText)) { matchResult = new MatchResult( roslynItem, item, matchedFilterText: matchedFilterText, - patternMatch: patternMatch, index: currentIndex++, GetHighlightedSpans()); + patternMatch: patternMatch, index: currentIndex++, GetHighlightedSpans(completionHelper, item, filterText, highlightMatchingPortions, roslynItem, patternMatch)); return true; } @@ -625,7 +643,13 @@ private static bool TryCreateMatchResult( matchResult = default; return false; - ImmutableArray GetHighlightedSpans() + static ImmutableArray GetHighlightedSpans( + CompletionHelper completionHelper, + VSCompletionItem item, + string filterText, + bool highlightMatchingPortions, + RoslynCompletionItem roslynItem, + PatternMatch? patternMatch) { if (!highlightMatchingPortions) { @@ -647,7 +671,7 @@ ImmutableArray GetHighlightedSpans() // Since VS item's display text is created as Prefix + DisplayText + Suffix, // we can calculate the highlighted span by adding an offset that is the length of the Prefix. return patternMatch.Value.MatchedSpans - .SelectAsArray(s => s.MoveTo(roslynItem.DisplayTextPrefix?.Length ?? 0).ToSpan()); + .SelectAsArray(s_highlightSpanGetter, roslynItem); } // If there's no match for Roslyn item's filter text which is identical to its display text, @@ -656,6 +680,10 @@ ImmutableArray GetHighlightedSpans() } } + // PERF: Create a singleton to avoid lambda allocation on hot path + private static readonly Func s_highlightSpanGetter + = (span, item) => span.MoveTo(item.DisplayTextPrefix?.Length ?? 0).ToSpan(); + private static bool MatchesFilterText( RoslynCompletionItem item, string filterText, diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index d6747d912d4b4..0bbf04fd43945 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -6090,6 +6090,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, False))) ' trigger completion with import completion disabled @@ -6160,6 +6161,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) ' trigger completion with import completion enabled @@ -6203,6 +6205,7 @@ namespace NS1 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) ' trigger completion with import completion enabled @@ -6638,6 +6641,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendInvokeCompletionList() @@ -6712,6 +6716,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendInvokeCompletionList() @@ -6786,6 +6791,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendInvokeCompletionList() @@ -6860,6 +6866,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendInvokeCompletionList() @@ -6935,6 +6942,7 @@ namespace NS2 Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendInvokeCompletionList() @@ -6980,6 +6988,7 @@ namespace OtherNS Dim workspace = state.Workspace workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendInvokeCompletionList() @@ -7099,6 +7108,7 @@ namespace NS2 Await state.AssertNoCompletionSession() workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, -1) _ .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True))) state.SendTypeChars("mytask") diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb index 50407021e6f82..c9cf512757760 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.vb @@ -14,14 +14,16 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Private Property IsExpandedCompletion As Boolean = True - Private Property ShowImportCompletionItemsOptionValue As Boolean = True - ' -1 would disable timebox, whereas 0 means always timeout. Private Property TimeoutInMilliseconds As Integer = -1 + Private Property ShowImportCompletionItemsOptionValue As Boolean = True + Protected Overrides Function WithChangedOptions(options As OptionSet) As OptionSet Return options _ - .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue).WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) + .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue) _ + .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) _ + .WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion, TimeoutInMilliseconds) End Function Protected Overrides Function GetComposition() As TestComposition diff --git a/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs b/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs index c00ff30598d20..e059d2fb688c3 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceOptions.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Completion @@ -21,5 +19,12 @@ public static readonly Option2 IsExpandedCompletion /// public static readonly Option2 DisallowAddingImports = new(nameof(CompletionServiceOptions), nameof(DisallowAddingImports), defaultValue: false); + + /// + /// Timeout value used for time-boxing completion of unimported extension methods. + /// Value less than 0 means no timebox; value == 0 means immediate timeout (for testing purpose) + /// + public static readonly Option2 TimeoutInMillisecondsForExtensionMethodImportCompletion + = new(nameof(CompletionServiceOptions), nameof(TimeoutInMillisecondsForExtensionMethodImportCompletion), defaultValue: 500); } } diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs index 8b46d2ea8c00c..a8efd8b6571f6 100644 --- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs +++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs @@ -31,8 +31,6 @@ namespace Microsoft.CodeAnalysis.Completion /// public abstract partial class CompletionServiceWithProviders : CompletionService, IEqualityComparer> { - private static readonly Func> s_createList = _ => new List(); - private readonly object _gate = new(); private readonly ConditionalWeakTable, StrongBox>> _projectCompletionProvidersMap @@ -436,8 +434,7 @@ private CompletionList MergeAndPruneCompletionLists( // changed it 'wins' and picks the span that will be used for all items in the completion // list. If no contexts changed it, then just use the default span provided by the service. var finalCompletionListSpan = completionContexts.FirstOrDefault(c => c.CompletionListSpan != defaultSpan)?.CompletionListSpan ?? defaultSpan; - - var displayNameToItemsMap = new Dictionary>(); + using var displayNameToItemsMap = new DisplayNameToItemsMap(this); CompletionItem suggestionModeItem = null; foreach (var context in completionContexts) @@ -447,55 +444,31 @@ private CompletionList MergeAndPruneCompletionLists( foreach (var item in context.Items) { Debug.Assert(item != null); - AddToDisplayMap(item, displayNameToItemsMap); + displayNameToItemsMap.Add(item); } // first one wins suggestionModeItem ??= context.SuggestionModeItem; } - if (displayNameToItemsMap.Count == 0) + if (displayNameToItemsMap.IsEmpty) { return CompletionList.Empty; } // TODO(DustinCa): Revisit performance of this. - var totalItems = displayNameToItemsMap.Values.Flatten().ToList(); - totalItems.Sort(); + using var _ = ArrayBuilder.GetInstance(displayNameToItemsMap.Count, out var builder); + builder.AddRange(displayNameToItemsMap); + builder.Sort(); return CompletionList.Create( finalCompletionListSpan, - totalItems.ToImmutableArray(), + builder.ToImmutable(), GetRules(), suggestionModeItem, isExclusive); } - private void AddToDisplayMap( - CompletionItem item, - Dictionary> displayNameToItemsMap) - { - var sameNamedItems = displayNameToItemsMap.GetOrAdd(item.GetEntireDisplayText(), s_createList); - - // If two items have the same display text choose which one to keep. - // If they don't actually match keep both. - - for (var i = 0; i < sameNamedItems.Count; i++) - { - var existingItem = sameNamedItems[i]; - - Debug.Assert(item.GetEntireDisplayText() == existingItem.GetEntireDisplayText()); - - if (ItemsMatch(item, existingItem)) - { - sameNamedItems[i] = GetBetterItem(item, existingItem); - return; - } - } - - sameNamedItems.Add(item); - } - /// /// Determines if the items are similar enough they should be represented by a single item in the list. /// @@ -650,6 +623,100 @@ int IEqualityComparer>.GetHashCode(ImmutableHashSet, IDisposable + { + // We might need to handle large amount of items with import completion enabled, + // so use a dedicated pool to minimize array allocations. + // Set the size of pool to a small number 5 because we don't expect more than a + // couple of callers at the same time. + private static readonly ObjectPool> s_uniqueSourcesPool + = new(factory: () => new(), size: 5); + + private readonly Dictionary _displayNameToItemsMap; + private readonly CompletionServiceWithProviders _service; + + public int Count { get; private set; } + + public DisplayNameToItemsMap(CompletionServiceWithProviders service) + { + _service = service; + _displayNameToItemsMap = s_uniqueSourcesPool.Allocate(); + } + + public void Dispose() + { + _displayNameToItemsMap.Clear(); + s_uniqueSourcesPool.Free(_displayNameToItemsMap); + } + + public bool IsEmpty => _displayNameToItemsMap.Count == 0; + + public void Add(CompletionItem item) + { + var entireDisplayText = item.GetEntireDisplayText(); + + if (!_displayNameToItemsMap.TryGetValue(entireDisplayText, out var value)) + { + Count++; + _displayNameToItemsMap.Add(entireDisplayText, item); + return; + } + + // If two items have the same display text choose which one to keep. + // If they don't actually match keep both. + if (value is CompletionItem sameNamedItem) + { + if (_service.ItemsMatch(item, sameNamedItem)) + { + _displayNameToItemsMap[entireDisplayText] = _service.GetBetterItem(item, sameNamedItem); + return; + } + + Count++; + // Matching items should be rare, no need to use object pool for this. + _displayNameToItemsMap[entireDisplayText] = new List() { sameNamedItem, item }; + } + else if (value is List sameNamedItems) + { + for (var i = 0; i < sameNamedItems.Count; i++) + { + var existingItem = sameNamedItems[i]; + if (_service.ItemsMatch(item, existingItem)) + { + sameNamedItems[i] = _service.GetBetterItem(item, existingItem); + return; + } + } + + Count++; + sameNamedItems.Add(item); + } + } + + public IEnumerator GetEnumerator() + { + foreach (var value in _displayNameToItemsMap.Values) + { + if (value is CompletionItem sameNamedItem) + { + yield return sameNamedItem; + } + else if (value is List sameNamedItems) + { + foreach (var item in sameNamedItems) + { + yield return item; + } + } + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + internal TestAccessor GetTestAccessor() => new(this); diff --git a/src/Features/Core/Portable/Completion/Log/CompletionProvidersLogger.cs b/src/Features/Core/Portable/Completion/Log/CompletionProvidersLogger.cs index b3d8aa35873b0..401a5664df625 100644 --- a/src/Features/Core/Portable/Completion/Log/CompletionProvidersLogger.cs +++ b/src/Features/Core/Portable/Completion/Log/CompletionProvidersLogger.cs @@ -37,6 +37,7 @@ internal enum ActionInfo ExtensionMethodCompletionCreateItemsTicks, CommitsOfExtensionMethodImportCompletionItem, ExtensionMethodCompletionPartialResultCount, + ExtensionMethodCompletionTimeoutCount, } internal static void LogTypeImportCompletionTicksDataPoint(int count) @@ -81,6 +82,9 @@ internal static void LogCommitOfExtensionMethodImportCompletionItem() => internal static void LogExtensionMethodCompletionPartialResultCount() => s_logAggregator.IncreaseCount((int)ActionInfo.ExtensionMethodCompletionPartialResultCount); + internal static void LogExtensionMethodCompletionTimeoutCount() => + s_logAggregator.IncreaseCount((int)ActionInfo.ExtensionMethodCompletionTimeoutCount); + internal static void ReportTelemetry() { Logger.Log(FunctionId.Intellisense_CompletionProviders_Data, KeyValueLogMessage.Create(m => diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractExtensionMethodImportCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractExtensionMethodImportCompletionProvider.cs index d27864a510f64..634be5d37e8f2 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractExtensionMethodImportCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractExtensionMethodImportCompletionProvider.cs @@ -37,13 +37,34 @@ protected override async Task AddCompletionItemsAsync( var syntaxFacts = completionContext.Document.GetRequiredLanguageService(); if (TryGetReceiverTypeSymbol(syntaxContext, syntaxFacts, cancellationToken, out var receiverTypeSymbol)) { - var items = await ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsAsync( + using var nestedTokenSource = new CancellationTokenSource(); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken); + + var getItemsTask = Task.Run(() => ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsAsync( completionContext.Document, completionContext.Position, receiverTypeSymbol, namespaceInScope, forceIndexCreation: isExpandedCompletion, - cancellationToken).ConfigureAwait(false); + linkedTokenSource.Token)); + + var timeoutInMilliseconds = completionContext.Options.GetOption(CompletionServiceOptions.TimeoutInMillisecondsForExtensionMethodImportCompletion); + + // Timebox is enabled if timeout value is >= 0 and we are not triggered via expander + if (timeoutInMilliseconds >= 0 && !isExpandedCompletion) + { + // timeout == 0 means immediate timeout (for testing purpose) + if (timeoutInMilliseconds == 0 || await Task.WhenAny(getItemsTask, Task.Delay(timeoutInMilliseconds, linkedTokenSource.Token)).ConfigureAwait(false) != getItemsTask) + { + nestedTokenSource.Cancel(); + CompletionProvidersLogger.LogExtensionMethodCompletionTimeoutCount(); + return; + } + } + + // Either the timebox is not enabled, so we need to wait until the operation for complete, + // or there's no timeout, and we now have all completion items ready. + var items = await getItemsTask.ConfigureAwait(false); var receiverTypeKey = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken); completionContext.AddItems(items.Select(i => Convert(i, receiverTypeKey))); diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index e4a3cfc92ad37..b33682b2ceefa 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -24,15 +24,23 @@ private readonly struct CacheEntry private ImmutableArray ItemInfos { get; } + /// + /// The number of items in this entry for types declared as public. + /// This is used to minimize memory allocation in case non-public items aren't needed. + /// + private int PublicItemCount { get; } + private CacheEntry( Checksum checksum, string language, - ImmutableArray items) + ImmutableArray items, + int publicItemCount) { Checksum = checksum; Language = language; ItemInfos = items; + PublicItemCount = publicItemCount; } public ImmutableArray GetItemsForContext( @@ -46,6 +54,19 @@ public ImmutableArray GetItemsForContext( var isSameLanguage = Language == language; using var _ = ArrayBuilder.GetInstance(out var builder); + // PERF: try set the capacity upfront to avoid allocation from Resize + if (!isAttributeContext) + { + if (isInternalsVisible) + { + builder.EnsureCapacity(ItemInfos.Length); + } + else + { + builder.EnsureCapacity(PublicItemCount); + } + } + foreach (var info in ItemInfos) { if (!info.IsPublic && !isInternalsVisible) @@ -106,6 +127,8 @@ public class Builder : IDisposable private readonly Checksum _checksum; private readonly EditorBrowsableInfo _editorBrowsableInfo; + private int _publicItemCount; + private readonly ArrayBuilder _itemsBuilder; public Builder(Checksum checksum, string language, string genericTypeSuffix, EditorBrowsableInfo editorBrowsableInfo) @@ -123,7 +146,8 @@ public CacheEntry ToReferenceCacheEntry() return new CacheEntry( _checksum, _language, - _itemsBuilder.ToImmutable()); + _itemsBuilder.ToImmutable(), + _publicItemCount); } public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool isPublic) @@ -159,6 +183,9 @@ public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool is CompletionItemFlags.CachedAndExpanded, extensionMethodData: null); + if (isPublic) + _publicItemCount++; + _itemsBuilder.Add(new TypeImportCompletionItemInfo(item, isPublic, isGeneric, isAttribute, isEditorBrowsableStateAdvanced)); } diff --git a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx index 2af97a6f65870..885f35be54a5f 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx +++ b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx @@ -577,7 +577,7 @@ When on multiple lines - Show items from unimported namespaces (experimental) + Show items from unimported namespaces Inside namespace diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf index 11170fd8031f2..78aebc327fcac 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Automaticky zobrazovat seznam dokončení v seznamech argumentů (experimentální) + Automaticky zobrazovat seznam dokončení v seznamech argumentů (experimentální) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Zobrazit položky z neimportovaných oborů názvů (experimentální) + Show items from unimported namespaces + Zobrazit položky z neimportovaných oborů názvů (experimentální) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf index 656e21ad2cc8b..a7e899fd66344 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Vervollständigungsliste in Argumentlisten automatisch anzeigen (experimentell) + Vervollständigungsliste in Argumentlisten automatisch anzeigen (experimentell) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Elemente aus nicht importierten Namespaces anzeigen (experimentell) + Show items from unimported namespaces + Elemente aus nicht importierten Namespaces anzeigen (experimentell) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf index 5018768973e71..e83793f12929d 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Mostrar automáticamente la lista de finalización en las listas de argumentos (experimental) + Mostrar automáticamente la lista de finalización en las listas de argumentos (experimental) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Mostrar elementos de espacios de nombres no importados (experimental) + Show items from unimported namespaces + Mostrar elementos de espacios de nombres no importados (experimental) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf index 02e49f719a1ca..212f41ab16b3f 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Afficher automatiquement la liste de complétion dans les listes d'arguments (expérimental) + Afficher automatiquement la liste de complétion dans les listes d'arguments (expérimental) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Afficher les éléments des espaces de noms qui ne sont pas importés (expérimental) + Show items from unimported namespaces + Afficher les éléments des espaces de noms qui ne sont pas importés (expérimental) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf index 55f3e9295e626..e796a13e1623a 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Mostra automaticamente l'elenco di completamento negli elenchi di argomenti (sperimentale) + Mostra automaticamente l'elenco di completamento negli elenchi di argomenti (sperimentale) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Mostra elementi da spazi dei nomi non importati (sperimentale) + Show items from unimported namespaces + Mostra elementi da spazi dei nomi non importati (sperimentale) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf index c9adbb0a26977..2d95b3fe9d967 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - 入力候補一覧を引数リストに自動的に表示する (試験段階) + 入力候補一覧を引数リストに自動的に表示する (試験段階) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - インポートされていない名前空間の項目を表示する (試験段階) + Show items from unimported namespaces + インポートされていない名前空間の項目を表示する (試験段階) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf index 62f39ec5d477d..4fa4148d45414 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - 인수 목록에 자동으로 완성 목록 표시(실험적) + 인수 목록에 자동으로 완성 목록 표시(실험적) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - 가져오지 않은 네임스페이스의 항목 표시(실험적) + Show items from unimported namespaces + 가져오지 않은 네임스페이스의 항목 표시(실험적) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf index 580a09dd78aec..c576960e0ec53 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Automatycznie pokaż listę uzupełniania na listach argumentów (eksperymentalne) + Automatycznie pokaż listę uzupełniania na listach argumentów (eksperymentalne) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Pokaż elementy z nieimportowanych przestrzeni nazw (eksperymentalne) + Show items from unimported namespaces + Pokaż elementy z nieimportowanych przestrzeni nazw (eksperymentalne) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf index 9b58fda3405ba..fd7c62d0d95e7 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Mostrar automaticamente a lista de conclusão nas listas de argumentos (experimental) + Mostrar automaticamente a lista de conclusão nas listas de argumentos (experimental) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Mostrar itens de namespaces não importados (experimental) + Show items from unimported namespaces + Mostrar itens de namespaces não importados (experimental) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf index 5cb450b09a611..4e4ae15d032ea 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Автоматически показывать список завершения в списках аргументов (экспериментальная функция) + Автоматически показывать список завершения в списках аргументов (экспериментальная функция) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - Показать элементы из неимпортированных пространств имен (экспериментальная функция) + Show items from unimported namespaces + Показать элементы из неимпортированных пространств имен (экспериментальная функция) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf index 91fa8b502244d..7f2e235362856 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - Bağımsız değişken listelerinde tamamlama listesini otomatik olarak göster (deneysel) + Bağımsız değişken listelerinde tamamlama listesini otomatik olarak göster (deneysel) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - İçeri aktarılmayan ad alanlarındaki öğeleri göster (deneysel) + Show items from unimported namespaces + İçeri aktarılmayan ad alanlarındaki öğeleri göster (deneysel) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf index 8637d5d4cad94..5b39d0b3ad730 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - 自动显示参数列表中的完成列表(实验性) + 自动显示参数列表中的完成列表(实验性) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - 显示 unimported 命名空间中的项(实验) + Show items from unimported namespaces + 显示 unimported 命名空间中的项(实验) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf index 7467b95fc3604..1423ee23659d4 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf @@ -9,7 +9,7 @@ Automatically show completion list in argument lists (experimental) - 自動在引數清單中顯示自動完成清單 (實驗性) + 自動在引數清單中顯示自動完成清單 (實驗性) @@ -128,8 +128,8 @@ - Show items from unimported namespaces (experimental) - 顯示來自未匯入命名空間的項目 (實驗性) + Show items from unimported namespaces + 顯示來自未匯入命名空間的項目 (實驗性) diff --git a/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx b/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx index 68d56673db587..8bb6a6ab74b26 100644 --- a/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx +++ b/src/VisualStudio/VisualBasic/Impl/BasicVSResources.resx @@ -296,7 +296,7 @@ Unused local - Show items from unimported namespaces (experimental) + Show items from unimported namespaces Show remarks in Quick Info diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.cs.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.cs.xlf index 039a498362c0c..45e1f88cc36fa 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.cs.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.cs.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Zobrazit položky z neimportovaných oborů názvů (experimentální) + Show items from unimported namespaces + Zobrazit položky z neimportovaných oborů názvů (experimentální) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.de.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.de.xlf index d2bd1e2ee7c5d..e97a679d6538f 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.de.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.de.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Elemente aus nicht importierten Namespaces anzeigen (experimentell) + Show items from unimported namespaces + Elemente aus nicht importierten Namespaces anzeigen (experimentell) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.es.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.es.xlf index 900daab7142e1..92c66dc6aff6f 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.es.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.es.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Mostrar elementos de espacios de nombres no importados (experimental) + Show items from unimported namespaces + Mostrar elementos de espacios de nombres no importados (experimental) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.fr.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.fr.xlf index 833e35c65c960..822ce328b74ce 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.fr.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.fr.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Afficher les éléments des espaces de noms qui ne sont pas importés (expérimental) + Show items from unimported namespaces + Afficher les éléments des espaces de noms qui ne sont pas importés (expérimental) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.it.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.it.xlf index 5409011ffb102..890bfdbb45b81 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.it.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.it.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Mostra elementi da spazi dei nomi non importati (sperimentale) + Show items from unimported namespaces + Mostra elementi da spazi dei nomi non importati (sperimentale) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ja.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ja.xlf index deb6a6d0b6919..da7f3f134ab3d 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ja.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ja.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - インポートされていない名前空間の項目を表示する (試験段階) + Show items from unimported namespaces + インポートされていない名前空間の項目を表示する (試験段階) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ko.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ko.xlf index 0a385fe2e4e3c..4db6f33b5dac1 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ko.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ko.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - 가져오지 않은 네임스페이스의 항목 표시(실험적) + Show items from unimported namespaces + 가져오지 않은 네임스페이스의 항목 표시(실험적) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pl.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pl.xlf index c20e6ed64cf3d..824e0a0f54207 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pl.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pl.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Pokaż elementy z nieimportowanych przestrzeni nazw (eksperymentalne) + Show items from unimported namespaces + Pokaż elementy z nieimportowanych przestrzeni nazw (eksperymentalne) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pt-BR.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pt-BR.xlf index 5f33df5d42e9b..f75bc40bf302f 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pt-BR.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.pt-BR.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Mostrar itens de namespaces não importados (experimental) + Show items from unimported namespaces + Mostrar itens de namespaces não importados (experimental) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ru.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ru.xlf index 34388dfd7b682..4884f549585e8 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ru.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.ru.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - Показать элементы из неимпортированных пространств имен (экспериментальная функция) + Show items from unimported namespaces + Показать элементы из неимпортированных пространств имен (экспериментальная функция) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.tr.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.tr.xlf index be1953574df2e..c9ef3568c3290 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.tr.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.tr.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - İçeri aktarılmayan ad alanlarındaki öğeleri göster (deneysel) + Show items from unimported namespaces + İçeri aktarılmayan ad alanlarındaki öğeleri göster (deneysel) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hans.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hans.xlf index 66c04dc0135aa..f50a0b75a3f01 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hans.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hans.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - 显示 unimported 命名空间中的项(实验) + Show items from unimported namespaces + 显示 unimported 命名空间中的项(实验) diff --git a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hant.xlf b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hant.xlf index 09c4764f4683e..4cd2cd4ed1d9a 100644 --- a/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hant.xlf +++ b/src/VisualStudio/VisualBasic/Impl/xlf/BasicVSResources.zh-Hant.xlf @@ -53,8 +53,8 @@ - Show items from unimported namespaces (experimental) - 顯示來自未匯入命名空間的項目 (實驗性) + Show items from unimported namespaces + 顯示來自未匯入命名空間的項目 (實驗性)