未验证 提交 21c8f4d7 编写于 作者: G Gen Lu 提交者: GitHub

Merge pull request #39813 from genlu/TypeImportPerf

Improvement for completion of unimported types
......@@ -23,16 +23,12 @@ public ExtensionMethodImportCompletionProviderTests(CSharpTestWorkspaceFixture w
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;
protected override void SetWorkspaceOptions(TestWorkspace workspace)
{
workspace.Options = workspace.Options
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, ShowImportCompletionItemsOptionValue)
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, TimeoutInMilliseconds)
.WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion);
}
......
......@@ -3,13 +3,9 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp.Completion.Providers;
using Microsoft.CodeAnalysis.Editor.Implementation.Interactive;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Experiments;
......@@ -34,14 +30,13 @@ internal override CompletionProvider CreateCompletionProvider()
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;
protected override void SetWorkspaceOptions(TestWorkspace workspace)
{
workspace.Options = workspace.Options
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, ShowImportCompletionItemsOptionValue)
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, TimeoutInMilliseconds);
.WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion);
}
protected override ExportProvider GetExportProvider()
......@@ -73,6 +68,7 @@ class Bar
public async Task OptionSetToNull_ExpDisabled()
{
ShowImportCompletionItemsOptionValue = null;
IsExpandedCompletion = false;
var markup = @"
class Bar
{
......@@ -90,6 +86,7 @@ public async Task OptionSetToFalse(bool isExperimentEnabled)
SetExperimentOption(WellKnownExperimentNames.TypeImportCompletion, isExperimentEnabled);
ShowImportCompletionItemsOptionValue = false;
IsExpandedCompletion = false;
var markup = @"
class Bar
......@@ -1252,32 +1249,6 @@ namespace Foo
await VerifyCustomCommitProviderAsync(markup, "MyClass", expectedCodeAfterCommit, sourceCodeKind: kind);
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(36624, "https://github.com/dotnet/roslyn/issues/36624")]
public async Task DoNotShowImportItemsIfTimeout()
{
// Set timeout to 0 so it always timeout
TimeoutInMilliseconds = 0;
var file1 = $@"
namespace NS1
{{
public class Bar
{{}}
}}";
var file2 = @"
namespace NS2
{
class C
{
$$
}
}";
var markup = CreateMarkupForSingleProject(file2, file1, LanguageNames.CSharp);
await VerifyTypeImportItemIsAbsentAsync(markup, "Bar", inlineDescription: "NS1");
}
[Fact]
[Trait(Traits.Feature, Traits.Features.Completion)]
[Trait(Traits.Feature, Traits.Features.Interactive)]
......
......@@ -8,7 +8,6 @@ Imports Microsoft.CodeAnalysis.CSharp
Imports Microsoft.CodeAnalysis.Editor.CSharp.Formatting
Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
Imports Microsoft.CodeAnalysis.Experiments
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Tags
......@@ -5088,60 +5087,7 @@ class C
End Using
End Function
<WorkItem(35614, "https://github.com/dotnet/roslyn/issues/35614")>
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestTypeImportCompletion() As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document><![CDATA[
namespace NS1
{
class C
{
public void Foo()
{
Bar$$
}
}
}
namespace NS2
{
public class Bar { }
}
]]></Document>)
Dim expectedText = "
using NS2;
namespace NS1
{
class C
{
public void Foo()
{
Bar
}
}
}
namespace NS2
{
public class Bar { }
}
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1) ' disable timebox for import completion
state.SendInvokeCompletionList()
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
End Function
<WpfFact(Skip:="https://github.com/dotnet/roslyn/issues/39070"), Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestExpanderWithImportCompletionDisabled() As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document><![CDATA[
......@@ -5168,10 +5114,7 @@ namespace NS2
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
Await state.AssertCompletionItemsDoNotContainAny({"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' select expander
' make sure expander is selected
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
......@@ -5194,69 +5137,24 @@ namespace NS2
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(
<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
' dismiss completion
state.SendEscape()
Await state.AssertNoCompletionSession()
' trigger completion with import completion enabled
' trigger completion again
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()
' should not show unimported item even with cache populated
Await state.AssertCompletionItemsDoNotContainAny({"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()
Await state.AssertCompletionItemsDoNotContainAny({"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestExpanderAndTimeboxWithImportCompletionEnabled() As Task
Public Async Function TestExpanderWithImportCompletionEnabled() As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document><![CDATA[
namespace NS1
......@@ -5276,71 +5174,30 @@ namespace NS2
}
]]></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)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
' 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)
' trigger completion with import completion enabled
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
Await state.AssertCompletionItemsDoNotContainAny({"Bar"})
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=False)
' select expander
' make sure expander is selected
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(
<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)
' dismiss completion
state.SendEscape()
Await state.AssertNoCompletionSession()
' trigger completion with import completion disabled
' trigger completion again
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
Await state.AssertCompletionItemsDoNotContainAny({"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)
' now cache is populated
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
......@@ -5762,11 +5619,19 @@ namespace NS2
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
' make sure expander is selected
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
Await state.AssertSelectedCompletionItem(displayText:="Bar", displayTextSuffix:="<>")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
......@@ -5825,11 +5690,19 @@ namespace NS2
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
' make sure expander is selected
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
Await state.AssertSelectedCompletionItem(displayText:="Bar")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
......@@ -5888,11 +5761,19 @@ namespace NS2
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
' make sure expander is selected
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
Await state.AssertSelectedCompletionItem(displayText:="ABar")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
......@@ -5951,10 +5832,17 @@ namespace NS2
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
' make sure expander is selected
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="")
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
......@@ -6016,11 +5904,19 @@ namespace NS2
"
state.Workspace.Options = state.Workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, -1)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, True)
state.SendInvokeCompletionList()
Await state.WaitForUIRenderedAsync()
' make sure expander is selected
state.SetCompletionItemExpanderState(isSelected:=True)
Await state.WaitForAsynchronousOperationsAsync()
Await state.WaitForUIRenderedAsync()
Await state.AssertSelectedCompletionItem(displayText:="Bar", inlineDescription:="NS2")
state.AssertCompletionItemExpander(isAvailable:=True, isSelected:=True)
state.SendTab()
Assert.Equal(expectedText, state.GetDocumentText())
End Using
......
......@@ -25,9 +25,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
Protected Overrides Sub SetWorkspaceOptions(workspace As TestWorkspace)
workspace.Options = workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, TimeoutInMilliseconds) _
.WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion)
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue).WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion)
End Sub
Protected Overrides Function GetExportProvider() As ExportProvider
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Completion.Providers
Imports Microsoft.CodeAnalysis.Editor.UnitTests
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Experiments
Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers
Imports Microsoft.VisualStudio.Composition
......@@ -20,13 +18,12 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
Private Property ShowImportCompletionItemsOptionValue As Boolean = True
' -1 would disable timebox, whereas 0 means always timeout.
Private Property TimeoutInMilliseconds As Integer = -1
Private Property IsExpandedCompletion As Boolean = True
Protected Overrides Sub SetWorkspaceOptions(workspace As TestWorkspace)
workspace.Options = workspace.Options _
.WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, ShowImportCompletionItemsOptionValue) _
.WithChangedOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion, TimeoutInMilliseconds)
.WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion)
End Sub
Protected Overrides Function GetExportProvider() As ExportProvider
......
......@@ -71,10 +71,10 @@ public sealed class CompletionContext
public bool IsExclusive { get; set; }
/// <summary>
/// Set to true if the corresponding provider can provide extended items with currect context,
/// Set to true if the corresponding provider can provide extended items with current 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.
/// completion list if we are in the appropriate context.
/// </summary>
internal bool ExpandItemsAvailable { get; set; }
......@@ -129,7 +129,7 @@ public void AddItems(IEnumerable<CompletionItem> items)
/// <summary>
/// An optional <see cref="CompletionItem"/> that appears selected in the list presented to the user during suggestion mode.
///
/// Suggestion mode disables autoselection of items in the list, giving preference to the text typed by the user unless a specific item is selected manually.
/// Suggestion mode disables auto-selection of items in the list, giving preference to the text typed by the user unless a specific item is selected manually.
///
/// Specifying a <see cref="SuggestionModeItem"/> is a request that the completion host operate in suggestion mode.
/// The item specified determines the text displayed and the description associated with it unless a different item is manually selected.
......
......@@ -11,13 +11,5 @@ internal static class CompletionServiceOptions
/// </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,
/// Therefore we set the timeout to 1s to ensure it only applies to the case that cache is cold.
/// </summary>
public static readonly Option<int> TimeoutInMillisecondsForImportCompletion
= new Option<int>(nameof(CompletionServiceOptions), nameof(TimeoutInMillisecondsForImportCompletion), defaultValue: 1000);
}
}
......@@ -20,12 +20,12 @@ internal enum ActionInfo
TypeImportCompletionTicks,
TypeImportCompletionItemCount,
TypeImportCompletionReferenceCount,
TypeImportCompletionTimeoutCount,
TypeImportCompletionCacheMissCount,
TargetTypeCompletionTicks,
ExtensionMethodCompletionSuccessCount,
// following are only reported when sucessful (i.e. filter is available)
// following are only reported when successful (i.e. filter is available)
ExtensionMethodCompletionTicks,
ExtensionMethodCompletionMethodsProvided,
ExtensionMethodCompletionGetFilterTicks,
......@@ -43,8 +43,8 @@ internal enum ActionInfo
internal static void LogTypeImportCompletionReferenceCountDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.TypeImportCompletionReferenceCount, count);
internal static void LogTypeImportCompletionTimeout() =>
s_logAggregator.IncreaseCount((int)ActionInfo.TypeImportCompletionTimeoutCount);
internal static void LogTypeImportCompletionCacheMiss() =>
s_logAggregator.IncreaseCount((int)ActionInfo.TypeImportCompletionCacheMissCount);
internal static void LogTargetTypeCompletionTicksDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.TargetTypeCompletionTicks, count);
......
......@@ -46,7 +46,7 @@ protected override bool ShouldProvideCompletion(Document document, SyntaxContext
else
{
// If we can't get a valid receiver type, then we don't show expander as available.
// We need to set this explicitly here bacause we didn't do the (more expensive) symbol check inside
// We need to set this explicitly here because we didn't do the (more expensive) symbol check inside
// `ShouldProvideCompletion` method above, which is intended for quick syntax based check.
completionContext.ExpandItemsAvailable = false;
}
......@@ -62,7 +62,7 @@ protected override bool ShouldProvideCompletion(Document document, SyntaxContext
var parentNode = syntaxContext.TargetToken.Parent;
// Even though implicit access to extension method is allowed, we decide not support it for simplicity
// e.g. we will not provide completion for unimport extension method in this case
// e.g. we will not provide completion for unimported extension method in this case
// New Bar() {.X = .$$ }
var expressionNode = syntaxFacts.GetLeftSideOfDot(parentNode, allowImplicitTarget: false);
......
......@@ -5,13 +5,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion.Log;
using Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
......@@ -24,113 +22,47 @@ protected override bool ShouldProvideCompletion(Document document, SyntaxContext
protected override async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet<string> namespacesInScope, bool isExpandedCompletion, CancellationToken cancellationToken)
{
using var _ = Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken);
var telemetryCounter = new TelemetryCounter();
var document = completionContext.Document;
var project = document.Project;
var workspace = project.Solution.Workspace;
var typeImportCompletionService = document.GetLanguageService<ITypeImportCompletionService>()!;
var tasksToGetCompletionItems = ArrayBuilder<Task<ImmutableArray<CompletionItem>>>.GetInstance();
// Get completion items from current project.
var compilation = (await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false))!;
tasksToGetCompletionItems.Add(Task.Run(() => typeImportCompletionService.GetTopLevelTypesAsync(
project,
syntaxContext,
isInternalsVisible: true,
cancellationToken)));
// Get declarations from directly referenced projects and PEs.
// For script compilation, we don't want previous submissions returned as referenced assemblies,
// there's no need to check for unimported type from them since namespace declaration is not allowed in script.
var referencedAssemblySymbols = compilation.GetReferencedAssemblySymbols(excludePreviousSubmissions: true);
// This can be parallelized because we don't add items to CompletionContext
// until all the collected tasks are completed.
tasksToGetCompletionItems.AddRange(
referencedAssemblySymbols.Select(symbol => Task.Run(() => HandleReferenceAsync(symbol))));
using (Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken))
{
var telemetryCounter = new TelemetryCounter();
var typeImportCompletionService = completionContext.Document.GetRequiredLanguageService<ITypeImportCompletionService>();
// 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());
var itemsFromAllAssemblies = await typeImportCompletionService.GetAllTopLevelTypesAsync(
completionContext.Document.Project,
syntaxContext,
forceCacheCreation: isExpandedCompletion,
cancellationToken).ConfigureAwait(false);
if (isExpandedCompletion ||
timeoutInMilliseconds != 0 && await Task.WhenAny(combinedTask, Task.Delay(timeoutInMilliseconds, cancellationToken)).ConfigureAwait(false) == combinedTask)
{
// 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)
if (itemsFromAllAssemblies == null)
{
AddItems(completionItems, completionContext, namespacesInScope, telemetryCounter);
telemetryCounter.CacheMiss = true;
}
}
else
{
// If timed out, we don't want to cancel the computation so next time the cache would be populated.
// We do not keep track if previous compuation for a given project/PE reference is still running. So there's a chance
// we queue same computation again later. However, we expect such computation for an individual reference to be relatively
// fast so the actual cycles wasted would be insignificant.
telemetryCounter.TimedOut = true;
}
telemetryCounter.ReferenceCount = referencedAssemblySymbols.Length;
telemetryCounter.Report();
return;
async Task<ImmutableArray<CompletionItem>> HandleReferenceAsync(IAssemblySymbol referencedAssemblySymbol)
{
cancellationToken.ThrowIfCancellationRequested();
// Skip reference with only non-global alias.
var metadataReference = compilation.GetMetadataReference(referencedAssemblySymbol);
if (metadataReference.Properties.Aliases.IsEmpty ||
metadataReference.Properties.Aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias))
else
{
var assemblyProject = project.Solution.GetProject(referencedAssemblySymbol, cancellationToken);
if (assemblyProject != null && assemblyProject.SupportsCompilation)
foreach (var items in itemsFromAllAssemblies)
{
return await typeImportCompletionService.GetTopLevelTypesAsync(
assemblyProject,
syntaxContext,
isInternalsVisible: compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(referencedAssemblySymbol),
cancellationToken).ConfigureAwait(false);
}
else if (metadataReference is PortableExecutableReference peReference)
{
return typeImportCompletionService.GetTopLevelTypesFromPEReference(
project.Solution,
compilation,
peReference,
syntaxContext,
isInternalsVisible: compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(referencedAssemblySymbol),
cancellationToken);
AddItems(items, completionContext, namespacesInScope, telemetryCounter);
}
}
return ImmutableArray<CompletionItem>.Empty;
telemetryCounter.Report();
}
}
static void AddItems(ImmutableArray<CompletionItem> items, CompletionContext completionContext, HashSet<string> namespacesInScope, TelemetryCounter counter)
private static void AddItems(ImmutableArray<CompletionItem> items, CompletionContext completionContext, HashSet<string> namespacesInScope, TelemetryCounter counter)
{
counter.ReferenceCount++;
foreach (var item in items)
{
foreach (var item in items)
var containingNamespace = ImportCompletionItem.GetContainingNamespace(item);
if (!namespacesInScope.Contains(containingNamespace))
{
var containingNamespace = ImportCompletionItem.GetContainingNamespace(item);
if (!namespacesInScope.Contains(containingNamespace))
{
// We can return cached item directly, item's span will be fixed by completion service.
// On the other hand, because of this (i.e. mutating the span of cached item for each run),
// the provider can not be used as a service by components that might be run in parallel
// with completion, which would be a race.
completionContext.AddItem(item);
counter.ItemsCount++; ;
}
// We can return cached item directly, item's span will be fixed by completion service.
// On the other hand, because of this (i.e. mutating the span of cached item for each run),
// the provider can not be used as a service by components that might be run in parallel
// with completion, which would be a race.
completionContext.AddItem(item);
counter.ItemsCount++; ;
}
}
}
......@@ -140,7 +72,7 @@ private class TelemetryCounter
protected int Tick { get; }
public int ItemsCount { get; set; }
public int ReferenceCount { get; set; }
public bool TimedOut { get; set; }
public bool CacheMiss { get; set; }
public TelemetryCounter()
{
......@@ -149,14 +81,16 @@ public TelemetryCounter()
public void Report()
{
var delta = Environment.TickCount - Tick;
CompletionProvidersLogger.LogTypeImportCompletionTicksDataPoint(delta);
CompletionProvidersLogger.LogTypeImportCompletionItemCountDataPoint(ItemsCount);
CompletionProvidersLogger.LogTypeImportCompletionReferenceCountDataPoint(ReferenceCount);
if (TimedOut)
if (CacheMiss)
{
CompletionProvidersLogger.LogTypeImportCompletionCacheMiss();
}
else
{
CompletionProvidersLogger.LogTypeImportCompletionTimeout();
var delta = Environment.TickCount - Tick;
CompletionProvidersLogger.LogTypeImportCompletionTicksDataPoint(delta);
CompletionProvidersLogger.LogTypeImportCompletionItemCountDataPoint(ItemsCount);
CompletionProvidersLogger.LogTypeImportCompletionReferenceCountDataPoint(ReferenceCount);
}
}
}
......
......@@ -43,7 +43,7 @@ internal abstract partial class AbstractTypeImportCompletionService
{
// We will need to adjust some items if the request is made in:
// 1. attribute context, then we will not show or complete with "Attribute" suffix.
// 2. a project with different langauge than when the cache entry was created,
// 2. a project with different language than when the cache entry was created,
// then we will change the generic suffix accordingly.
// Otherwise, we can simply return cached items.
var isSameLanguage = Language == language;
......
......@@ -5,17 +5,24 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion
{
internal abstract partial class AbstractTypeImportCompletionService : ITypeImportCompletionService
{
private static readonly object s_gate = new object();
private static Task s_cachingTask = Task.CompletedTask;
private IImportCompletionCacheService<CacheEntry, CacheEntry> CacheService { get; }
protected abstract string GenericTypeSuffix { get; }
......@@ -27,108 +34,208 @@ internal AbstractTypeImportCompletionService(Workspace workspace)
CacheService = workspace.Services.GetRequiredService<IImportCompletionCacheService<CacheEntry, CacheEntry>>();
}
public async Task<ImmutableArray<CompletionItem>> GetTopLevelTypesAsync(
Project project,
public async Task<ImmutableArray<ImmutableArray<CompletionItem>>?> GetAllTopLevelTypesAsync(
Project currentProject,
SyntaxContext syntaxContext,
bool isInternalsVisible,
bool forceCacheCreation,
CancellationToken cancellationToken)
{
if (!project.SupportsCompilation)
var getCacheResults = await GetCacheEntries(currentProject, syntaxContext, forceCacheCreation, cancellationToken).ConfigureAwait(false);
if (getCacheResults == null)
{
throw new ArgumentException(nameof(project));
// We use a very simple approach to build the cache in the background:
// queue a new task only if the previous task is completed, regardless of what
// that task is doing.
lock (s_gate)
{
if (s_cachingTask.IsCompleted)
{
s_cachingTask = Task.Run(() => GetCacheEntries(currentProject, syntaxContext, forceCacheCreation: true, CancellationToken.None));
}
}
return null;
}
var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
return getCacheResults.Value.SelectAsArray(GetItemsFromCacheResult);
ImmutableArray<CompletionItem> GetItemsFromCacheResult(GetCacheResult cacheResult)
{
return cacheResult.Entry.GetItemsForContext(
syntaxContext.SemanticModel.Language,
GenericTypeSuffix,
currentCompilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(cacheResult.Assembly),
syntaxContext.IsAttributeNameContext,
IsCaseSensitive);
}
}
private async Task<ImmutableArray<GetCacheResult>?> GetCacheEntries(Project currentProject, SyntaxContext syntaxContext, bool forceCacheCreation, CancellationToken cancellationToken)
{
var _ = ArrayBuilder<GetCacheResult>.GetInstance(out var builder);
var cacheResult = await GetCacheForProject(currentProject, syntaxContext, forceCacheCreation: true, cancellationToken).ConfigureAwait(false);
// We always force create a cache for current project.
Debug.Assert(cacheResult.HasValue);
builder.Add(cacheResult!.Value);
var solution = currentProject.Solution;
var graph = solution.GetProjectDependencyGraph();
var referencedProjects = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id).SelectAsArray(id => solution.GetRequiredProject(id));
var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
foreach (var referencedProject in referencedProjects.Where(p => p.SupportsCompilation))
{
var compilation = await referencedProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var assembly = SymbolFinder.FindSimilarSymbols(compilation.Assembly, currentCompilation).SingleOrDefault();
var metadataReference = currentCompilation.GetMetadataReference(assembly);
if (HasGlobalAlias(metadataReference))
{
cacheResult = await GetCacheForProject(
referencedProject,
syntaxContext,
forceCacheCreation,
cancellationToken).ConfigureAwait(false);
if (cacheResult.HasValue)
{
builder.Add(cacheResult.Value);
}
else
{
// If there's cache miss, we just don't return any item.
// This way, we will not block completion building our cache.
return null;
}
}
}
foreach (var peReference in currentProject.MetadataReferences.OfType<PortableExecutableReference>())
{
if (HasGlobalAlias(peReference) &&
currentCompilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assembly &&
TryGetCacheForPEReference(solution, currentCompilation, peReference, syntaxContext, forceCacheCreation, cancellationToken, out cacheResult))
{
if (cacheResult.HasValue)
{
builder.Add(cacheResult.Value);
}
else
{
// If there's cache miss, we just don't return any item.
// This way, we will not block completion building our cache.
return null;
}
}
}
return builder.ToImmutable();
static bool HasGlobalAlias(MetadataReference metadataReference)
=> metadataReference != null && (metadataReference.Properties.Aliases.IsEmpty || metadataReference.Properties.Aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias));
}
/// <summary>
/// Get appropriate completion items for all the visible top level types from given project.
/// This method is intended to be used for getting types from source only, so the project must support compilation.
/// For getting types from PE, use <see cref="TryGetCacheForPEReference"/>.
/// </summary>
private async Task<GetCacheResult?> GetCacheForProject(
Project project,
SyntaxContext syntaxContext,
bool forceCacheCreation,
CancellationToken cancellationToken)
{
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
// Since we only need top level types from source, therefore we only care if source symbol checksum changes.
var checksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false);
return GetAccessibleTopLevelTypesWorker(
return GetCacheWorker(
project.Id,
compilation.Assembly,
checksum,
syntaxContext,
isInternalsVisible,
forceCacheCreation,
CacheService.ProjectItemsCache,
cancellationToken);
}
public ImmutableArray<CompletionItem> GetTopLevelTypesFromPEReference(
/// <summary>
/// Get appropriate completion items for all the visible top level types from given PE reference.
/// </summary>
private bool TryGetCacheForPEReference(
Solution solution,
Compilation compilation,
PortableExecutableReference peReference,
SyntaxContext syntaxContext,
bool isInternalsVisible,
CancellationToken cancellationToken)
bool forceCacheCreation,
CancellationToken cancellationToken,
out GetCacheResult? result)
{
var key = GetReferenceKey(peReference);
var key = peReference.FilePath ?? peReference.Display;
if (key == null)
{
// Can't cache items for reference with null key. We don't want risk potential perf regression by
// making those items repeatedly, so simply not returning anything from this assembly, until
// we have a better understanding on this sceanrio.
// we have a better understanding on this scenario.
// TODO: Add telemetry
return ImmutableArray<CompletionItem>.Empty;
result = default;
return false;
}
if (!(compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assemblySymbol))
{
return ImmutableArray<CompletionItem>.Empty;
result = default;
return false;
}
var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, peReference, cancellationToken);
return GetAccessibleTopLevelTypesWorker(
result = GetCacheWorker(
key,
assemblySymbol,
checksum,
syntaxContext,
isInternalsVisible,
forceCacheCreation,
CacheService.PEItemsCache,
cancellationToken);
static string GetReferenceKey(PortableExecutableReference reference)
=> reference.FilePath ?? reference.Display;
return true;
}
private ImmutableArray<CompletionItem> GetAccessibleTopLevelTypesWorker<TKey>(
TKey key,
IAssemblySymbol assembly,
Checksum checksum,
SyntaxContext syntaxContext,
bool isInternalsVisible,
IDictionary<TKey, CacheEntry> cache,
CancellationToken cancellationToken)
{
var cacheEntry = GetCacheEntry(key, assembly, checksum, syntaxContext, cache, cancellationToken);
return cacheEntry.GetItemsForContext(
syntaxContext.SemanticModel.Language,
GenericTypeSuffix,
isInternalsVisible,
syntaxContext.IsAttributeNameContext,
IsCaseSensitive);
}
private CacheEntry GetCacheEntry<TKey>(
// Returns null if cache miss and forceCacheCreation == false
private GetCacheResult? GetCacheWorker<TKey>(
TKey key,
IAssemblySymbol assembly,
Checksum checksum,
SyntaxContext syntaxContext,
bool forceCacheCreation,
IDictionary<TKey, CacheEntry> cache,
CancellationToken cancellationToken)
{
var language = syntaxContext.SemanticModel.Language;
// Cache miss, create all requested items.
if (!cache.TryGetValue(key, out var cacheEntry) ||
cacheEntry.Checksum != checksum)
// Cache hit
if (cache.TryGetValue(key, out var cacheEntry) && cacheEntry.Checksum == checksum)
{
return new GetCacheResult(cacheEntry, assembly);
}
// Cache miss, create all items only when asked.
if (forceCacheCreation)
{
using var builder = new CacheEntry.Builder(checksum, language, GenericTypeSuffix);
GetCompletionItemsForTopLevelTypeDeclarations(assembly.GlobalNamespace, builder, cancellationToken);
cacheEntry = builder.ToReferenceCacheEntry();
cache[key] = cacheEntry;
return new GetCacheResult(cacheEntry, assembly);
}
return cacheEntry;
return null;
}
private static void GetCompletionItemsForTopLevelTypeDeclarations(
......@@ -231,6 +338,18 @@ public TypeOverloadInfo Aggregate(INamedTypeSymbol type)
}
}
private readonly struct GetCacheResult
{
public CacheEntry Entry { get; }
public IAssemblySymbol Assembly { get; }
public GetCacheResult(CacheEntry entry, IAssemblySymbol assembly)
{
Entry = entry;
Assembly = assembly;
}
}
private readonly struct TypeImportCompletionItemInfo
{
private readonly ItemPropertyKind _properties;
......
......@@ -95,7 +95,7 @@ internal static partial class ExtensionMethodImportCompletionHelper
allTypeNamesBuilder.AddRange(receiverTypeSymbol.GetBaseTypes().Select(t => t.MetadataName));
allTypeNamesBuilder.AddRange(receiverTypeSymbol.GetAllInterfacesIncludingThis().Select(t => t.MetadataName));
// interface doesn't inherit from object, but is implicitly convertable to object type.
// interface doesn't inherit from object, but is implicitly convertible to object type.
if (receiverTypeSymbol.IsInterfaceType())
{
allTypeNamesBuilder.Add(nameof(Object));
......@@ -274,7 +274,7 @@ internal static partial class ExtensionMethodImportCompletionHelper
//
// TODO:
// Alternatively, we can try to get symbols out of each assembly, instead of the compilation (which includes all references),
// to avoid such conflict. We should give this approach a try and see if any perf improvement can be archieved.
// to avoid such conflict. We should give this approach a try and see if any perf improvement can be achieved.
var containerSymbol = compilation.GetTypeByMetadataName(fullyQualifiedContainerName);
if (containerSymbol != null)
......
......@@ -13,25 +13,18 @@ namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion
internal interface ITypeImportCompletionService : ILanguageService
{
/// <summary>
/// Get approperiate completion items for all the visible top level types from given project.
/// This method is intended to be used for getting types from source only, so the project must support compilation.
/// For getting types from PE, use <see cref="GetTopLevelTypesFromPEReference"/>.
/// Get completion items for all the accessible top level types from the given project and all its references.
/// Each array returned contains all items from one of the reachable entities (i.e. projects and PE references.)
/// Returns null if we don't have all the items cached and <paramref name="forceCacheCreation"/> is false.
/// </summary>
Task<ImmutableArray<CompletionItem>> GetTopLevelTypesAsync(
/// <remarks>
/// Because items from each entity are cached as a separate array, we simply return them as is instead of an
/// aggregated array to avoid unnecessary allocations.
/// </remarks>
Task<ImmutableArray<ImmutableArray<CompletionItem>>?> GetAllTopLevelTypesAsync(
Project project,
SyntaxContext syntaxContext,
bool isInternalsVisible,
CancellationToken cancellationToken);
/// <summary>
/// Get approperiate completion items for all the visible top level types from given PE reference.
/// </summary>
ImmutableArray<CompletionItem> GetTopLevelTypesFromPEReference(
Solution solution,
Compilation compilation,
PortableExecutableReference peReference,
SyntaxContext syntaxContext,
bool isInternalsVisible,
bool forceCacheCreation,
CancellationToken cancellationToken);
}
}
......@@ -16,5 +16,16 @@ public static void WriteTo(this IObjectWritable @object, ObjectWriter writer)
public static Document GetRequiredDocument(this Solution solution, SyntaxTree syntaxTree)
=> solution.GetDocument(syntaxTree) ?? throw new InvalidOperationException();
public static Project GetRequiredProject(this Solution solution, ProjectId projectId)
{
var project = solution.GetProject(projectId);
if (project == null)
{
throw new InvalidOperationException(string.Format(WorkspacesResources.Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution, projectId));
}
return project;
}
}
}
......@@ -1384,6 +1384,16 @@ internal class WorkspacesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Project of ID {0} is required to accomplish the task but is not available from the solution.
/// </summary>
internal static string Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution {
get {
return ResourceManager.GetString("Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_" +
"solution", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Property.
/// </summary>
......
......@@ -1485,4 +1485,7 @@ Zero-width positive lookbehind assertions are typically used at the beginning of
<data name="SyntaxTree_is_required_to_accomplish_the_task_but_is_not_supported_by_document_0" xml:space="preserve">
<value>Syntax tree is required to accomplish the task but is not supported by document {0}.</value>
</data>
<data name="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution" xml:space="preserve">
<value>Project of ID {0} is required to accomplish the task but is not available from the solution</value>
</data>
</root>
\ No newline at end of file
......@@ -107,6 +107,11 @@
<target state="translated">Předpona {0} se neočekávala.</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Pouze refaktoring</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">Das Präfix "{0}" wurde nicht erwartet.</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Nur Refactoring</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">No se espera el prefijo "{0}"</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Solo refactorización</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">Le préfixe « {0} » n'est pas attendu</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Refactorisation uniquement</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">Il prefisso '{0}' non è previsto</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Solo refactoring</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">プレフィックス '{0}' は予期されていません</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">リファクタリングのみ</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">접두사 '{0}'은(는) 사용할 수 없습니다.</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">리팩터링만</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">Nieoczekiwany prefiks „{0}”</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Tylko refaktoryzacja</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">O prefixo '{0}' não é esperado</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Somente Refatoração</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">Префикс "{0}" является недопустимым</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Только рефакторинг</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">'{0}' öneki beklenmez</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">Sadece Yeniden Düzenlenme</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">前缀“{0}”不是预期的</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">仅重构</target>
......
......@@ -107,6 +107,11 @@
<target state="translated">不應為前置詞 '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Project_of_ID_0_is_required_to_accomplish_the_task_but_is_not_available_from_the_solution">
<source>Project of ID {0} is required to accomplish the task but is not available from the solution</source>
<target state="new">Project of ID {0} is required to accomplish the task but is not available from the solution</target>
<note />
</trans-unit>
<trans-unit id="Refactoring_Only">
<source>Refactoring Only</source>
<target state="translated">僅重構</target>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册