未验证 提交 995e88f2 编写于 作者: D David Poeschl 提交者: GitHub

Merge pull request #35539 from YairHalberstadt/generate-parameter-code-fix

Add Generate Parameter Code Fix
......@@ -25,6 +25,8 @@ public class GenerateVariableTests : AbstractCSharpDiagnosticProviderBasedUserDi
private const int ReadonlyFieldIndex = 1;
private const int PropertyIndex = 2;
private const int LocalIndex = 3;
private const int Parameter = 4;
private const int ParameterAndOverrides = 5;
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new CSharpGenerateVariableCodeFixProvider());
......@@ -216,7 +218,27 @@ void Method(int i)
[|goo|] = 1;
}
}",
new[] { string.Format(FeaturesResources.Generate_field_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_property_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_local_0, "goo") });
new[] { string.Format(FeaturesResources.Generate_field_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_property_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_local_0, "goo"), string.Format(FeaturesResources.Generate_parameter_0, "goo") });
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestSimpleWriteInOverrideCount()
{
await TestExactActionSetOfferedAsync(
@"
abstract class Base
{
public abstract void Method(int i);
}
class Class : Base
{
public override void Method(int i)
{
[|goo|] = 1;
}
}",
new[] { string.Format(FeaturesResources.Generate_field_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_property_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_local_0, "goo"), string.Format(FeaturesResources.Generate_parameter_0, "goo"), string.Format(FeaturesResources.Generate_parameter_0_and_overrides_implementations, "goo") });
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
......@@ -371,7 +393,7 @@ void Method(out int i)
Method(out [|goo|]);
}
}",
new[] { string.Format(FeaturesResources.Generate_field_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_local_0, "goo") });
new[] { string.Format(FeaturesResources.Generate_field_1_0, "goo", "Class"), string.Format(FeaturesResources.Generate_local_0, "goo"), string.Format(FeaturesResources.Generate_parameter_0, "goo") });
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
......@@ -2001,7 +2023,7 @@ static void Main()
[|p|]++;
}
}",
new[] { string.Format(FeaturesResources.Generate_field_1_0, "p", "Program"), string.Format(FeaturesResources.Generate_property_1_0, "p", "Program"), string.Format(FeaturesResources.Generate_local_0, "p") });
new[] { string.Format(FeaturesResources.Generate_field_1_0, "p", "Program"), string.Format(FeaturesResources.Generate_property_1_0, "p", "Program"), string.Format(FeaturesResources.Generate_local_0, "p"), string.Format(FeaturesResources.Generate_parameter_0, "p") });
await TestInRegularAndScriptAsync(
@"class Program
......@@ -4513,7 +4535,7 @@ void Goo()
#line hidden
}
";
await TestExactActionSetOfferedAsync(code, new[] { string.Format(FeaturesResources.Generate_local_0, "Bar") });
await TestExactActionSetOfferedAsync(code, new[] { string.Format(FeaturesResources.Generate_local_0, "Bar"), string.Format(FeaturesResources.Generate_parameter_0, "Bar") });
await TestInRegularAndScriptAsync(code,
@"
......@@ -8940,5 +8962,143 @@ class Blah
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestAddParameter()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
[|goo|];
}
}",
@"class Class
{
void Method(object goo)
{
goo;
}
}", index: Parameter);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestAddParameter_DoesntAddToInterface()
{
await TestInRegularAndScriptAsync(
@"interface Interface
{
void Method();
}
class Class
{
public void Method()
{
[|goo|];
}
}",
@"interface Interface
{
void Method();
}
class Class
{
public void Method(object goo)
{
[|goo|];
}
}", index: Parameter);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestAddParameterAndOverrides_AddsToInterface()
{
await TestInRegularAndScriptAsync(
@"interface Interface
{
void Method();
}
class Class : Interface
{
public void Method()
{
[|goo|];
}
}",
@"interface Interface
{
void Method(object goo);
}
class Class : Interface
{
public void Method(object goo)
{
[|goo|];
}
}", index: ParameterAndOverrides);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestAddParameterIsOfCorrectType()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
M1([|goo|]);
}
void M1(int a);
}",
@"class Class
{
void Method(int goo)
{
M1(goo);
}
void M1(int a);
}", index: Parameter);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestAddParameterAndOverrides_IsOfCorrectType()
{
await TestInRegularAndScriptAsync(
@"interface Interface
{
void Method();
}
class Class : Interface
{
public void Method()
{
M1([|goo|]);
}
void M1(int a);
}",
@"interface Interface
{
void Method(int goo);
}
class Class : Interface
{
public void Method(int goo)
{
M1(goo);
}
void M1(int a);
}", index: ParameterAndOverrides);
}
}
}
......@@ -2623,7 +2623,7 @@ End Class")
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestPreferReadOnlyIfAfterReadOnlyAssignment() As Task
await TestInRegularAndScriptAsync(
Await TestInRegularAndScriptAsync(
"class C
private readonly _goo as integer
......@@ -2667,7 +2667,7 @@ end class")
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestPlaceFieldBasedOnSurroundingStatements() As Task
await TestInRegularAndScriptAsync(
Await TestInRegularAndScriptAsync(
"class Class
private _goo as integer
private _quux as integer
......@@ -2831,5 +2831,150 @@ End Module",
End Module",
index:=2)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameter() As Task
Await TestInRegularAndScriptAsync(
"Module Program
Sub Main(args As String())
Goo([|bar|])
End Sub
End Module",
"Module Program
Sub Main(args As String(), bar As Object)
Goo(bar)
End Sub
End Module",
index:=4)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameterDoesntAddToOverride() As Task
Await TestInRegularAndScriptAsync(
"Class Base
Public Overridable Sub Method(args As String())
End Sub
End Class
Class Program
Public Overrides Sub Main(args As String())
Goo([|bar|])
End Sub
End Class",
"Class Base
Public Overridable Sub Method(args As String())
End Sub
End Class
Class Program
Public Overrides Sub Main(args As String(), bar As Object)
Goo(bar)
End Sub
End Class",
index:=4)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameterAndOverridesAddsToOverrides() As Task
Await TestInRegularAndScriptAsync(
"Class Base
Public Overridable Sub Method(args As String())
End Sub
End Class
Class Program
Inherits Base
Public Overrides Sub Method(args As String())
Goo([|bar|])
End Sub
End Class",
"Class Base
Public Overridable Sub Method(args As String(), bar As Object)
End Sub
End Class
Class Program
Inherits Base
Public Overrides Sub Method(args As String(), bar As Object)
Goo(bar)
End Sub
End Class",
index:=5)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameterIsOfCorrectType() As Task
Await TestInRegularAndScriptAsync(
"Module Program
Sub Main(args As String())
Goo([|bar|])
End Sub
Sub Goo(arg As Integer)
End Sub
End Module",
"Module Program
Sub Main(args As String(), bar As Integer)
Goo(bar)
End Sub
Sub Goo(arg As Integer)
End Sub
End Module",
index:=4)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameterAndOverridesIsOfCorrectType() As Task
Await TestInRegularAndScriptAsync(
"Class Base
Public Overridable Sub Method(args As String())
End Sub
End Class
Class Program
Inherits Base
Public Overrides Sub Method(args As String())
Goo([|bar|])
End Sub
Sub Goo(arg As Integer)
End Sub
End Class",
"Class Base
Public Overridable Sub Method(args As String(), bar As Integer)
End Sub
End Class
Class Program
Inherits Base
Public Overrides Sub Method(args As String(), bar As Integer)
Goo(bar)
End Sub
Sub Goo(arg As Integer)
End Sub
End Class",
index:=5)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameterAndOverridesNotOfferedToNonOverride1() As Task
Await TestActionCountAsync(
"Module Program
Sub Main(args As String())
Goo([|bar|])
End Sub
End Module",
count:=5)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)>
Public Async Function TestAddParameterAndOverridesNotOfferedToNonOverride2() As Task
Await TestActionCountAsync(
"Class Base
Public Overridable Sub Method(args As String())
End Sub
End Class
Class Program
Inherits Base
Public Sub Method(args As String())
Goo([|bar|])
End Sub
Sub Goo(arg As Integer)
End Sub
End Class",
count:=5)
End Function
End Class
End Namespace
......@@ -317,7 +317,7 @@ ImmutableArray<CodeAction> NestByCascading()
var methodToUpdate = argumentInsertPositionData.MethodToUpdate;
var argumentToInsert = argumentInsertPositionData.ArgumentToInsert;
var cascadingFix = HasCascadingDeclarations(methodToUpdate)
var cascadingFix = AddParameterService.Instance.HasCascadingDeclarations(methodToUpdate)
? new Func<CancellationToken, Task<Solution>>(c => FixAsync(document, methodToUpdate, argumentToInsert, arguments, fixAllReferences: true, c))
: null;
......@@ -332,52 +332,6 @@ ImmutableArray<CodeAction> NestByCascading()
return builder.ToImmutableAndFree();
}
/// <summary>
/// Checks if there are indications that there might be more than one declarations that need to be fixed.
/// The check does not look-up if there are other declarations (this is done later in the CodeAction).
/// </summary>
private bool HasCascadingDeclarations(IMethodSymbol method)
{
// Don't cascade constructors
if (method.IsConstructor())
{
return false;
}
// Virtual methods of all kinds might have overrides somewhere else that need to be fixed.
if (method.IsVirtual || method.IsOverride || method.IsAbstract)
{
return true;
}
// If interfaces are involved we will fix those too
// Explicit interface implementations are easy to detect
if (method.ExplicitInterfaceImplementations.Length > 0)
{
return true;
}
// For implicit interface implementations lets check if the characteristic of the method
// allows it to implicit implement an interface member.
if (method.DeclaredAccessibility != Accessibility.Public)
{
return false;
}
if (method.IsStatic)
{
return false;
}
// Now check if the method does implement an interface member
if (method.ExplicitOrImplicitInterfaceImplementations().Length > 0)
{
return true;
}
return false;
}
private static string GetCodeFixTitle(string resourceString, IMethodSymbol methodToUpdate, bool includeParameters)
{
var methodDisplay = methodToUpdate.ToDisplayString(new SymbolDisplayFormat(
......@@ -404,7 +358,6 @@ private static string GetCodeFixTitle(string resourceString, IMethodSymbol metho
bool fixAllReferences,
CancellationToken cancellationToken)
{
var solution = invocationDocument.Project.Solution;
var (argumentType, refKind) = await GetArgumentTypeAndRefKindAsync(invocationDocument, argument, cancellationToken).ConfigureAwait(false);
// The argumentNameSuggestion is the base for the parameter name.
......@@ -412,66 +365,16 @@ private static string GetCodeFixTitle(string resourceString, IMethodSymbol metho
var (argumentNameSuggestion, isNamedArgument) = await GetNameSuggestionForArgumentAsync(
invocationDocument, argument, cancellationToken).ConfigureAwait(false);
var referencedSymbols = fixAllReferences
? await FindMethodDeclarationReferences(invocationDocument, method, cancellationToken).ConfigureAwait(false)
: method.GetAllMethodSymbolsOfPartialParts();
var anySymbolReferencesNotInSource = referencedSymbols.Any(symbol => !symbol.IsFromSource());
var locationsInSource = referencedSymbols.Where(symbol => symbol.IsFromSource());
// Indexing Locations[0] is valid because IMethodSymbols have one location at most
// and IsFromSource() tests if there is at least one location.
var locationsByDocument = locationsInSource.ToLookup(declarationLocation
=> solution.GetDocument(declarationLocation.Locations[0].SourceTree));
foreach (var documentLookup in locationsByDocument)
{
var document = documentLookup.Key;
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(syntaxRoot, solution.Workspace);
var generator = editor.Generator;
foreach (var methodDeclaration in documentLookup)
{
var methodNode = syntaxRoot.FindNode(methodDeclaration.Locations[0].SourceSpan);
var existingParameters = generator.GetParameters(methodNode);
var insertionIndex = isNamedArgument
? existingParameters.Count
: argumentList.IndexOf(argument);
// if the preceding parameter is optional, the new parameter must also be optional
// see also BC30202 and CS1737
var parameterMustBeOptional = insertionIndex > 0 &&
syntaxFacts.GetDefaultOfParameter(existingParameters[insertionIndex - 1]) != null;
var parameterSymbol = CreateParameterSymbol(
methodDeclaration, argumentType, refKind, parameterMustBeOptional, argumentNameSuggestion);
var argumentInitializer = parameterMustBeOptional ? generator.DefaultExpression(argumentType) : null;
var parameterDeclaration = generator.ParameterDeclaration(parameterSymbol, argumentInitializer)
.WithAdditionalAnnotations(Formatter.Annotation);
if (anySymbolReferencesNotInSource && methodDeclaration == method)
{
parameterDeclaration = parameterDeclaration.WithAdditionalAnnotations(
ConflictAnnotation.Create(FeaturesResources.Related_method_signatures_found_in_metadata_will_not_be_updated));
}
if (method.MethodKind == MethodKind.ReducedExtension)
{
insertionIndex++;
}
AddParameter(
syntaxFacts, editor, methodNode, argument,
insertionIndex, parameterDeclaration, cancellationToken);
}
var newRoot = editor.GetChangedRoot();
solution = solution.WithDocumentSyntaxRoot(document.Id, newRoot);
}
return solution;
var newParameterIndex = isNamedArgument ? (int?)null : argumentList.IndexOf(argument);
return await AddParameterService.Instance.AddParameterAsync(
invocationDocument,
method,
argumentType,
refKind,
argumentNameSuggestion,
newParameterIndex,
fixAllReferences,
cancellationToken).ConfigureAwait(false);
}
private static async Task<(ITypeSymbol, RefKind)> GetArgumentTypeAndRefKindAsync(Document invocationDocument, TArgumentSyntax argument, CancellationToken cancellationToken)
......@@ -484,25 +387,6 @@ private static async Task<(ITypeSymbol, RefKind)> GetArgumentTypeAndRefKindAsync
return (argumentType, refKind);
}
private static async Task<ImmutableArray<IMethodSymbol>> FindMethodDeclarationReferences(
Document invocationDocument, IMethodSymbol method, CancellationToken cancellationToken)
{
var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance);
await SymbolFinder.FindReferencesAsync(
symbolAndProjectId: SymbolAndProjectId.Create(method, invocationDocument.Project.Id),
solution: invocationDocument.Project.Solution,
documents: null,
progress: progress,
options: FindReferencesSearchOptions.Default,
cancellationToken: cancellationToken).ConfigureAwait(false);
var referencedSymbols = progress.GetReferencedSymbols();
return referencedSymbols.Select(referencedSymbol => referencedSymbol.Definition)
.OfType<IMethodSymbol>()
.Distinct()
.ToImmutableArray();
}
private async Task<(string argumentNameSuggestion, bool isNamed)> GetNameSuggestionForArgumentAsync(
Document invocationDocument, TArgumentSyntax argument, CancellationToken cancellationToken)
{
......@@ -524,178 +408,6 @@ private static async Task<(ITypeSymbol, RefKind)> GetArgumentTypeAndRefKindAsync
}
}
private IParameterSymbol CreateParameterSymbol(
IMethodSymbol method,
ITypeSymbol parameterType,
RefKind refKind,
bool isOptional,
string argumentNameSuggestion)
{
var uniqueName = NameGenerator.EnsureUniqueness(argumentNameSuggestion, method.Parameters.Select(p => p.Name));
var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: default, refKind: refKind, isOptional: isOptional, isParams: false, type: parameterType, name: uniqueName);
return newParameterSymbol;
}
private static void AddParameter(
ISyntaxFactsService syntaxFacts,
SyntaxEditor editor,
SyntaxNode declaration,
TArgumentSyntax argument,
int insertionIndex,
SyntaxNode parameterDeclaration,
CancellationToken cancellationToken)
{
var sourceText = declaration.SyntaxTree.GetText(cancellationToken);
var generator = editor.Generator;
var existingParameters = generator.GetParameters(declaration);
var placeOnNewLine = ShouldPlaceParametersOnNewLine(existingParameters, cancellationToken);
if (!placeOnNewLine)
{
// Trivial case. Just let the stock editor impl handle this for us.
editor.InsertParameter(declaration, insertionIndex, parameterDeclaration);
return;
}
if (insertionIndex == existingParameters.Count)
{
// Placing the last parameter on its own line. Get the indentation of the
// curent last parameter and give the new last parameter the same indentation.
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[existingParameters.Count - 1], includeLeadingNewLine: true);
parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation)
.WithAdditionalAnnotations(Formatter.Annotation);
editor.AddParameter(declaration, parameterDeclaration);
}
else if (insertionIndex == 0)
{
// Inserting into the start of the list. The existing first parameter might
// be on the same line as the parameter list, or it might be on the next line.
var firstParameter = existingParameters[0];
var previousToken = firstParameter.GetFirstToken().GetPreviousToken();
if (sourceText.AreOnSameLine(previousToken, firstParameter.GetFirstToken()))
{
// First parameter is on hte same line as the method.
// We want to insert the parameter at the front of the exsiting parameter
// list. That means we need to move the current first parameter to a new
// line. Give the current first parameter the indentation of the second
// parameter in the list.
editor.InsertParameter(declaration, insertionIndex, parameterDeclaration);
var nextParameter = existingParameters[insertionIndex];
var nextLeadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[insertionIndex + 1], includeLeadingNewLine: true);
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(nextLeadingIndentation)
.WithAdditionalAnnotations(Formatter.Annotation));
}
else
{
// First parameter is on its own line. No need to adjust its indentation.
// Just copy its indentation over to the parameter we're inserting, and
// make sure the current first parameter gets a newline so it stays on
// its own line.
// We want to insert the parameter at the front of the exsiting parameter
// list. That means we need to move the current first parameter to a new
// line. Give the current first parameter the indentation of the second
// parameter in the list.
var firstLeadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[0], includeLeadingNewLine: false);
editor.InsertParameter(declaration, insertionIndex,
parameterDeclaration.WithLeadingTrivia(firstLeadingIndentation));
var nextParameter = existingParameters[insertionIndex];
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed)
.WithAdditionalAnnotations(Formatter.Annotation));
}
}
else
{
// We're inserting somewhere after the start (but not at the end). Because
// we've set placeOnNewLine, we know that the current comma we'll be placed
// after already have a newline following it. So all we need for this new
// parameter is to get the indentation of the following parameter.
// Because we're going to 'steal' the existing comma from that parameter,
// ensure that the next parameter has a new-line added to it so that it will
// still stay on a new line.
var nextParameter = existingParameters[insertionIndex];
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[insertionIndex], includeLeadingNewLine: false);
parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation);
editor.InsertParameter(declaration, insertionIndex, parameterDeclaration);
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed)
.WithAdditionalAnnotations(Formatter.Annotation));
}
}
private static List<SyntaxTrivia> GetDesiredLeadingIndentation(
SyntaxGenerator generator, ISyntaxFactsService syntaxFacts,
SyntaxNode node, bool includeLeadingNewLine)
{
var triviaList = new List<SyntaxTrivia>();
if (includeLeadingNewLine)
{
triviaList.Add(generator.ElasticCarriageReturnLineFeed);
}
var lastWhitespace = default(SyntaxTrivia);
foreach (var trivia in node.GetLeadingTrivia().Reverse())
{
if (syntaxFacts.IsWhitespaceTrivia(trivia))
{
lastWhitespace = trivia;
}
else if (syntaxFacts.IsEndOfLineTrivia(trivia))
{
break;
}
}
if (lastWhitespace.RawKind != 0)
{
triviaList.Add(lastWhitespace);
}
return triviaList;
}
private static bool ShouldPlaceParametersOnNewLine(
IReadOnlyList<SyntaxNode> parameters, CancellationToken cancellationToken)
{
if (parameters.Count <= 1)
{
return false;
}
var text = parameters[0].SyntaxTree.GetText(cancellationToken);
for (int i = 1, n = parameters.Count; i < n; i++)
{
var lastParameter = parameters[i - 1];
var thisParameter = parameters[i];
if (text.AreOnSameLine(lastParameter.GetLastToken(), thisParameter.GetFirstToken()))
{
return false;
}
}
// All parameters are on different lines. Place the new parameter on a new line as well.
return true;
}
private static readonly SymbolDisplayFormat SimpleFormat =
new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Microsoft.CodeAnalysis.AddParameter
{
internal class AddParameterService : IAddParameterService
{
private AddParameterService()
{
}
public static AddParameterService Instance = new AddParameterService();
public bool HasCascadingDeclarations(IMethodSymbol method)
{
// Don't cascade constructors
if (method.IsConstructor())
{
return false;
}
// Virtual methods of all kinds might have overrides somewhere else that need to be fixed.
if (method.IsVirtual || method.IsOverride || method.IsAbstract)
{
return true;
}
// If interfaces are involved we will fix those too
// Explicit interface implementations are easy to detect
if (method.ExplicitInterfaceImplementations.Length > 0)
{
return true;
}
// For implicit interface implementations lets check if the characteristic of the method
// allows it to implicit implement an interface member.
if (method.DeclaredAccessibility != Accessibility.Public)
{
return false;
}
if (method.IsStatic)
{
return false;
}
// Now check if the method does implement an interface member
if (method.ExplicitOrImplicitInterfaceImplementations().Length > 0)
{
return true;
}
return false;
}
public async Task<Solution> AddParameterAsync(
Document invocationDocument,
IMethodSymbol method,
ITypeSymbol newParamaterType,
RefKind refKind,
string parameterName,
int? newParameterIndex,
bool fixAllReferences,
CancellationToken cancellationToken)
{
var solution = invocationDocument.Project.Solution;
var referencedSymbols = fixAllReferences
? await FindMethodDeclarationReferences(invocationDocument, method, cancellationToken).ConfigureAwait(false)
: method.GetAllMethodSymbolsOfPartialParts();
var anySymbolReferencesNotInSource = referencedSymbols.Any(symbol => !symbol.IsFromSource());
var locationsInSource = referencedSymbols.Where(symbol => symbol.IsFromSource());
// Indexing Locations[0] is valid because IMethodSymbols have one location at most
// and IsFromSource() tests if there is at least one location.
var locationsByDocument = locationsInSource.ToLookup(declarationLocation
=> solution.GetDocument(declarationLocation.Locations[0].SourceTree));
foreach (var documentLookup in locationsByDocument)
{
var document = documentLookup.Key;
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(syntaxRoot, solution.Workspace);
var generator = editor.Generator;
foreach (var methodDeclaration in documentLookup)
{
var methodNode = syntaxRoot.FindNode(methodDeclaration.Locations[0].SourceSpan);
var existingParameters = generator.GetParameters(methodNode);
var insertionIndex = newParameterIndex ?? existingParameters.Count;
// if the preceding parameter is optional, the new parameter must also be optional
// see also BC30202 and CS1737
var parameterMustBeOptional = insertionIndex > 0 &&
syntaxFacts.GetDefaultOfParameter(existingParameters[insertionIndex - 1]) != null;
var parameterSymbol = CreateParameterSymbol(
methodDeclaration, newParamaterType, refKind, parameterMustBeOptional, parameterName);
var argumentInitializer = parameterMustBeOptional ? generator.DefaultExpression(newParamaterType) : null;
var parameterDeclaration = generator.ParameterDeclaration(parameterSymbol, argumentInitializer)
.WithAdditionalAnnotations(Formatter.Annotation);
if (anySymbolReferencesNotInSource && methodDeclaration == method)
{
parameterDeclaration = parameterDeclaration.WithAdditionalAnnotations(
ConflictAnnotation.Create(FeaturesResources.Related_method_signatures_found_in_metadata_will_not_be_updated));
}
if (method.MethodKind == MethodKind.ReducedExtension)
{
insertionIndex++;
}
AddParameter(syntaxFacts, editor, methodNode, insertionIndex, parameterDeclaration, cancellationToken);
}
var newRoot = editor.GetChangedRoot();
solution = solution.WithDocumentSyntaxRoot(document.Id, newRoot);
}
return solution;
}
private static async Task<ImmutableArray<IMethodSymbol>> FindMethodDeclarationReferences(
Document invocationDocument, IMethodSymbol method, CancellationToken cancellationToken)
{
var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance);
await SymbolFinder.FindReferencesAsync(
symbolAndProjectId: SymbolAndProjectId.Create(method, invocationDocument.Project.Id),
solution: invocationDocument.Project.Solution,
documents: null,
progress: progress,
options: FindReferencesSearchOptions.Default,
cancellationToken: cancellationToken).ConfigureAwait(false);
var referencedSymbols = progress.GetReferencedSymbols();
return referencedSymbols.Select(referencedSymbol => referencedSymbol.Definition)
.OfType<IMethodSymbol>()
.Distinct()
.ToImmutableArray();
}
private IParameterSymbol CreateParameterSymbol(
IMethodSymbol method,
ITypeSymbol parameterType,
RefKind refKind,
bool isOptional,
string argumentNameSuggestion)
{
var uniqueName = NameGenerator.EnsureUniqueness(argumentNameSuggestion, method.Parameters.Select(p => p.Name));
var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: default, refKind: refKind, isOptional: isOptional, isParams: false, type: parameterType, name: uniqueName);
return newParameterSymbol;
}
private static void AddParameter(
ISyntaxFactsService syntaxFacts,
SyntaxEditor editor,
SyntaxNode declaration,
int insertionIndex,
SyntaxNode parameterDeclaration,
CancellationToken cancellationToken)
{
var sourceText = declaration.SyntaxTree.GetText(cancellationToken);
var generator = editor.Generator;
var existingParameters = generator.GetParameters(declaration);
var placeOnNewLine = ShouldPlaceParametersOnNewLine(existingParameters, cancellationToken);
if (!placeOnNewLine)
{
// Trivial case. Just let the stock editor impl handle this for us.
editor.InsertParameter(declaration, insertionIndex, parameterDeclaration);
return;
}
if (insertionIndex == existingParameters.Count)
{
// Placing the last parameter on its own line. Get the indentation of the
// curent last parameter and give the new last parameter the same indentation.
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[existingParameters.Count - 1], includeLeadingNewLine: true);
parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation)
.WithAdditionalAnnotations(Formatter.Annotation);
editor.AddParameter(declaration, parameterDeclaration);
}
else if (insertionIndex == 0)
{
// Inserting into the start of the list. The existing first parameter might
// be on the same line as the parameter list, or it might be on the next line.
var firstParameter = existingParameters[0];
var previousToken = firstParameter.GetFirstToken().GetPreviousToken();
if (sourceText.AreOnSameLine(previousToken, firstParameter.GetFirstToken()))
{
// First parameter is on hte same line as the method.
// We want to insert the parameter at the front of the exsiting parameter
// list. That means we need to move the current first parameter to a new
// line. Give the current first parameter the indentation of the second
// parameter in the list.
editor.InsertParameter(declaration, insertionIndex, parameterDeclaration);
var nextParameter = existingParameters[insertionIndex];
var nextLeadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[insertionIndex + 1], includeLeadingNewLine: true);
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(nextLeadingIndentation)
.WithAdditionalAnnotations(Formatter.Annotation));
}
else
{
// First parameter is on its own line. No need to adjust its indentation.
// Just copy its indentation over to the parameter we're inserting, and
// make sure the current first parameter gets a newline so it stays on
// its own line.
// We want to insert the parameter at the front of the exsiting parameter
// list. That means we need to move the current first parameter to a new
// line. Give the current first parameter the indentation of the second
// parameter in the list.
var firstLeadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[0], includeLeadingNewLine: false);
editor.InsertParameter(declaration, insertionIndex,
parameterDeclaration.WithLeadingTrivia(firstLeadingIndentation));
var nextParameter = existingParameters[insertionIndex];
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed)
.WithAdditionalAnnotations(Formatter.Annotation));
}
}
else
{
// We're inserting somewhere after the start (but not at the end). Because
// we've set placeOnNewLine, we know that the current comma we'll be placed
// after already have a newline following it. So all we need for this new
// parameter is to get the indentation of the following parameter.
// Because we're going to 'steal' the existing comma from that parameter,
// ensure that the next parameter has a new-line added to it so that it will
// still stay on a new line.
var nextParameter = existingParameters[insertionIndex];
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[insertionIndex], includeLeadingNewLine: false);
parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation);
editor.InsertParameter(declaration, insertionIndex, parameterDeclaration);
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed)
.WithAdditionalAnnotations(Formatter.Annotation));
}
}
private static List<SyntaxTrivia> GetDesiredLeadingIndentation(
SyntaxGenerator generator, ISyntaxFactsService syntaxFacts,
SyntaxNode node, bool includeLeadingNewLine)
{
var triviaList = new List<SyntaxTrivia>();
if (includeLeadingNewLine)
{
triviaList.Add(generator.ElasticCarriageReturnLineFeed);
}
var lastWhitespace = default(SyntaxTrivia);
foreach (var trivia in node.GetLeadingTrivia().Reverse())
{
if (syntaxFacts.IsWhitespaceTrivia(trivia))
{
lastWhitespace = trivia;
}
else if (syntaxFacts.IsEndOfLineTrivia(trivia))
{
break;
}
}
if (lastWhitespace.RawKind != 0)
{
triviaList.Add(lastWhitespace);
}
return triviaList;
}
private static bool ShouldPlaceParametersOnNewLine(
IReadOnlyList<SyntaxNode> parameters, CancellationToken cancellationToken)
{
if (parameters.Count <= 1)
{
return false;
}
var text = parameters[0].SyntaxTree.GetText(cancellationToken);
for (int i = 1, n = parameters.Count; i < n; i++)
{
var lastParameter = parameters[i - 1];
var thisParameter = parameters[i];
if (text.AreOnSameLine(lastParameter.GetLastToken(), thisParameter.GetFirstToken()))
{
return false;
}
}
// All parameters are on different lines. Place the new parameter on a new line as well.
return true;
}
}
}
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.AddParameter
{
internal interface IAddParameterService
{
/// <summary>
/// Checks if there are indications that there might be more than one declarations that need to be fixed.
/// The check does not look-up if there are other declarations (this is done later in the CodeAction).
/// </summary>
bool HasCascadingDeclarations(IMethodSymbol method);
/// <summary>
/// Adds a parameter to a method.
/// </summary>
/// <param name="newParameterIndex"><see langword="null"/> to add as the final parameter</param>
/// <returns></returns>
Task<Solution> AddParameterAsync(
Document invocationDocument,
IMethodSymbol method,
ITypeSymbol newParamaterType,
RefKind refKind,
string parameterName,
int? newParameterIndex,
bool fixAllReferences,
CancellationToken cancellationToken);
}
}
......@@ -1695,6 +1695,24 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Generate parameter &apos;{0}&apos;.
/// </summary>
internal static string Generate_parameter_0 {
get {
return ResourceManager.GetString("Generate_parameter_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Generate parameter &apos;{0}&apos; (and overrides/implementations).
/// </summary>
internal static string Generate_parameter_0_and_overrides_implementations {
get {
return ResourceManager.GetString("Generate_parameter_0_and_overrides_implementations", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Generate property &apos;{1}.{0}&apos;.
/// </summary>
......
......@@ -1623,6 +1623,12 @@ This version used in: {2}</value>
<data name="Target_type_matches" xml:space="preserve">
<value>Target type matches</value>
</data>
<data name="Generate_parameter_0" xml:space="preserve">
<value>Generate parameter '{0}'</value>
</data>
<data name="Generate_parameter_0_and_overrides_implementations" xml:space="preserve">
<value>Generate parameter '{0}' (and overrides/implementations)</value>
</data>
<data name="in_Source_attribute" xml:space="preserve">
<value>in Source (attribute)</value>
</data>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddParameter;
using Microsoft.CodeAnalysis.CodeActions;
namespace Microsoft.CodeAnalysis.GenerateMember.GenerateVariable
{
internal partial class AbstractGenerateVariableService<TService, TSimpleNameSyntax, TExpressionSyntax>
{
private class GenerateParameterCodeAction : CodeAction
{
private readonly Document _document;
private readonly State _state;
private readonly bool _includeOverridesAndImplementations;
public GenerateParameterCodeAction(Document document, State state, bool includeOverridesAndImplementations)
{
_document = document;
_state = state;
_includeOverridesAndImplementations = includeOverridesAndImplementations;
}
public override string Title
{
get
{
var text = _includeOverridesAndImplementations
? FeaturesResources.Generate_parameter_0_and_overrides_implementations
: FeaturesResources.Generate_parameter_0;
return string.Format(
text,
_state.IdentifierToken.ValueText);
}
}
protected override Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
return AddParameterService.Instance.AddParameterAsync(
_document,
_state.ContainingMethod,
_state.LocalType,
RefKind.None,
_state.IdentifierToken.ValueText,
newParameterIndex: null,
_includeOverridesAndImplementations,
cancellationToken);
}
}
}
}
......@@ -21,6 +21,7 @@ private partial class State
{
public INamedTypeSymbol ContainingType { get; private set; }
public INamedTypeSymbol TypeToGenerateIn { get; private set; }
public IMethodSymbol ContainingMethod { get; private set; }
public bool IsStatic { get; private set; }
public bool IsConstant { get; private set; }
public bool IsIndexer { get; private set; }
......@@ -133,9 +134,16 @@ private partial class State
internal bool CanGenerateLocal()
{
// !this.IsInMemberContext prevents us offering this fix for `x.goo` where `goo` does not exist
return !this.IsInMemberContext && this.IsInExecutableBlock;
}
internal bool CanGenerateParameter()
{
// !this.IsInMemberContext prevents us offering this fix for `x.goo` where `goo` does not exist
return this.ContainingMethod != null && !this.IsInMemberContext && !this.IsConstant;
}
private bool TryInitializeExplicitInterface(
TService service,
SemanticDocument document,
......@@ -261,6 +269,8 @@ internal bool CanGenerateLocal()
this.IsInMemberContext = this.SimpleNameOpt != this.SimpleNameOrMemberAccessExpressionOpt ||
syntaxFacts.IsObjectInitializerNamedAssignmentIdentifier(this.SimpleNameOrMemberAccessExpressionOpt);
this.ContainingMethod = semanticModel.GetEnclosingSymbol<IMethodSymbol>(this.IdentifierToken.SpanStart, cancellationToken);
CheckSurroundingContext(semanticDocument, SymbolKind.Field, cancellationToken);
CheckSurroundingContext(semanticDocument, SymbolKind.Property, cancellationToken);
......
......@@ -4,6 +4,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddParameter;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Internal.Log;
......@@ -49,30 +50,27 @@ protected AbstractGenerateVariableService()
var canGenerateMember = CodeGenerator.CanAdd(document.Project.Solution, state.TypeToGenerateIn, cancellationToken);
// prefer fields over properties (and vice versa) depending on the casing of the member.
// lowercase -> fields. title case -> properties.
var name = state.IdentifierToken.ValueText;
if (char.IsUpper(name.ToCharArray().FirstOrDefault()))
if (canGenerateMember)
{
if (canGenerateMember)
// prefer fields over properties (and vice versa) depending on the casing of the member.
// lowercase -> fields. title case -> properties.
var name = state.IdentifierToken.ValueText;
if (char.IsUpper(name.ToCharArray().FirstOrDefault()))
{
AddPropertyCodeActions(actions, semanticDocument, state);
AddFieldCodeActions(actions, semanticDocument, state);
}
AddLocalCodeActions(actions, document, state);
}
else
{
if (canGenerateMember)
else
{
AddFieldCodeActions(actions, semanticDocument, state);
AddPropertyCodeActions(actions, semanticDocument, state);
}
AddLocalCodeActions(actions, document, state);
}
AddLocalCodeActions(actions, document, state);
AddParameterCodeActions(actions, document, state);
if (actions.Count > 1)
{
// Wrap the generate variable actions into a single top level suggestion
......@@ -177,6 +175,17 @@ private void AddLocalCodeActions(ArrayBuilder<CodeAction> result, Document docum
}
}
private void AddParameterCodeActions(ArrayBuilder<CodeAction> result, Document document, State state)
{
if (state.CanGenerateParameter())
{
result.Add(new GenerateParameterCodeAction(document, state, includeOverridesAndImplementations: false));
if (AddParameterService.Instance.HasCascadingDeclarations(state.ContainingMethod))
result.Add(new GenerateParameterCodeAction(document, state, includeOverridesAndImplementations: true));
}
}
private RefKind GetRefKindFromContext(State state)
{
if (state.IsInRefContext)
......
......@@ -137,6 +137,16 @@
<target state="translated">Formátuje se dokument.</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Odsadit všechny argumenty</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Dokument wird formatiert</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Alle Argumente einrücken</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Aplicando formato al documento</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Aplicar sangría a todos los argumentos</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Mise en forme du document</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Mettre en retrait tous les arguments</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Formattazione del documento</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Imposta rientro per tutti gli argomenti</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">ドキュメントの書式設定</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">すべての引数にインデントを設定します</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">문서 서식을 지정하는 중</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">모든 인수 들여쓰기</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Trwa formatowanie dokumentu...</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Dodaj wcięcie dla wszystkich argumentów</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Formatando documento</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Recuar todos os argumentos</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Форматирование документа</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Добавить отступы для всех аргументов</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">Belge biçimlendiriliyor</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">Tüm bağımsız değişkenleri girintile</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">设置文档格式</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">缩进所有参数</target>
......
......@@ -137,6 +137,16 @@
<target state="translated">正在將文件格式化</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0">
<source>Generate parameter '{0}'</source>
<target state="new">Generate parameter '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_parameter_0_and_overrides_implementations">
<source>Generate parameter '{0}' (and overrides/implementations)</source>
<target state="new">Generate parameter '{0}' (and overrides/implementations)</target>
<note />
</trans-unit>
<trans-unit id="Indent_all_arguments">
<source>Indent all arguments</source>
<target state="translated">將所有引數縮排</target>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册