提交 a0754a99 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #14884 from CyrusNajmabadi/convertToInterpolated2

Add feature to convert concatenated strings to interpolated strings.
......@@ -139,6 +139,7 @@
<Compile Include="Classification\TotalClassifierTests.cs" />
<Compile Include="Classification\TotalClassifierTests_Dynamic.cs" />
<Compile Include="CodeActions\AbstractCSharpCodeActionTest.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertConcatenationToInterpolatedStringTests.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertPlaceholderToInterpolatedStringTests.cs" />
<Compile Include="CodeActions\EncapsulateField\EncapsulateFieldTests.cs" />
<Compile Include="CodeActions\ExtractMethod\ExtractMethodTests.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertToInterpolatedString
{
public class ConvertConcatenationToInterpolatedStringTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace)
=> new CSharpConvertConcatenationToInterpolatedStringRefactoringProvider();
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingOnSimpleString()
{
await TestMissingAsync(
@"
public class C
{
void M()
{
var v = [||]""string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingOnConcatenatedStrings1()
{
await TestMissingAsync(
@"
public class C
{
void M()
{
var v = [||]""string"" + ""string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingOnConcatenatedStrings2()
{
await TestMissingAsync(
@"
public class C
{
void M()
{
var v = ""string"" + [||]""string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithStringOnLeft()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = [||]""string"" + 1;
}
}",
@"
public class C
{
void M()
{
var v = $""string{1}"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestRightSideOfString()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = ""string""[||] + 1;
}
}",
@"
public class C
{
void M()
{
var v = $""string{1}"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithStringOnRight()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = 1 + [||]""string"";
}
}",
@"
public class C
{
void M()
{
var v = $""{1}string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithComplexExpressionOnLeft()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = 1 + 2 + [||]""string"";
}
}",
@"
public class C
{
void M()
{
var v = $""{1 + 2}string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithTrivia1()
{
await TestAsync(
@"
public class C
{
void M()
{
var v =
// Leading trivia
1 + 2 + [||]""string"" /* trailing trivia */;
}
}",
@"
public class C
{
void M()
{
var v =
// Leading trivia
$""{1 + 2}string"" /* trailing trivia */;
}
}", compareTokens: false);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithComplexExpressions()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = 1 + 2 + [||]""string"" + 3 + 4;
}
}",
@"
public class C
{
void M()
{
var v = $""{1 + 2}string{3}{4}"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithEscapes1()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = ""\r"" + 2 + [||]""string"" + 3 + ""\n"";
}
}",
@"
public class C
{
void M()
{
var v = $""\r{2}string{3}\n"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithEscapes2()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = ""\\r"" + 2 + [||]""string"" + 3 + ""\\n"";
}
}",
@"
public class C
{
void M()
{
var v = $""\\r{2}string{3}\\n"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithVerbatimString1()
{
await TestAsync(
@"
public class C
{
void M()
{
var v = 1 + [||]@""string"";
}
}",
@"
public class C
{
void M()
{
var v = $@""{1}string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingWithMixedStringTypes1()
{
await TestMissingAsync(
@"
public class C
{
void M()
{
var v = 1 + [||]@""string"" + 2 + ""string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestMissingWithMixedStringTypes2()
{
await TestMissingAsync(
@"
public class C
{
void M()
{
var v = 1 + @""string"" + 2 + [||]""string"";
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithOverloadedOperator()
{
await TestAsync(
@"
public class D
{
public static bool operator+(D d, string s) => false;
public static bool operator+(string s, D d) => false;
}
public class C
{
void M()
{
D d = null;
var v = 1 + [||]""string"" + d;
}
}",
@"
public class D
{
public static bool operator+(D d, string s) => false;
public static bool operator+(string s, D d) => false;
}
public class C
{
void M()
{
D d = null;
var v = $""{1}string"" + d;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)]
public async Task TestWithOverloadedOperator2()
{
await TestMissingAsync(
@"
public class D
{
public static bool operator+(D d, string s) => false;
public static bool operator+(string s, D d) => false;
}
public class C
{
void M()
{
D d = null;
var v = d + [||]""string"" + 1;
}
}");
}
}
}
\ No newline at end of file
......@@ -623,6 +623,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Designer</SubType>
</None>
<Compile Include="ConvertToInterpolatedString\ConvertConcatenationToInterpolatedStringTests.vb" />
<Content Include="PerfTests\Sources\BasicPgoTypingInput.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
......
' 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.CodeRefactorings
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings
Imports Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ConvertToInterpolatedString
Public Class ConvertConcatenationToInterpolatedStringTests
Inherits AbstractVisualBasicCodeActionTest
Protected Overrides Function CreateCodeRefactoringProvider(workspace As Workspace) As CodeRefactoringProvider
Return New VisualBasicConvertConcatenationToInterpolatedStringRefactoringProvider()
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestMissingOnSimpleString() As Task
Await TestMissingAsync(
"
Public Class C
Sub M()
dim v = [||]""string""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithStringOnLeft() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = [||]""string"" & 1
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""string{1}""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestRightSideOfString() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = ""string""[||] & 1
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""string{1}""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithStringOnRight() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = 1 & [||]""string""
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""{1}string""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithComplexExpressionOnLeft() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = 1 + 2 & [||]""string""
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""{1 + 2}string""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithTrivia1() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = 1 + 2 & [||]""string"" ' trailing trivia
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""{1 + 2}string"" ' trailing trivia
End Sub
End Class", compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithComplexExpressions() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = 1 + 2 & [||]""string"" & 3 & 4
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""{1 + 2}string{3}{4}""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithEscapes1() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = ""\r"" & 2 & [||]""string"" & 3 & ""\n""
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""\r{2}string{3}\n""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithEscapes2() As Task
Await TestAsync(
"
Public Class C
Sub M()
dim v = ""\\r"" & 2 & [||]""string"" & 3 & ""\\n""
End Sub
End Class",
"
Public Class C
Sub M()
dim v = $""\\r{2}string{3}\\n""
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithOverloadedOperator() As Task
Await TestAsync(
"
public class D
public shared operator&(D d, string s) as boolean
end operator
public shared operator&(string s, D d) as boolean
end operator
end class
Public Class C
Sub M()
dim d as D = nothing
dim v = 1 & [||]""string"" & d
End Sub
End Class",
"
public class D
public shared operator&(D d, string s) as boolean
end operator
public shared operator&(string s, D d) as boolean
end operator
end class
Public Class C
Sub M()
dim d as D = nothing
dim v = $""{1}string"" & d
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)>
Public Async Function TestWithOverloadedOperator2() As Task
Await TestMissingAsync(
"
public class D
public shared operator&(D d, string s) as boolean
end operator
public shared operator&(string s, D d) as boolean
end operator
end class
Public Class C
Sub M()
dim d as D = nothing
dim v = d & [||]""string"" & 1
End Sub
End Class")
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -55,6 +55,7 @@
<Compile Include="..\..\..\Compilers\CSharp\Portable\Syntax\LambdaUtilities.cs">
<Link>InternalUtilities\LambdaUtilities.cs</Link>
</Compile>
<Compile Include="ConvertToInterpolatedString\CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="UseCoalesceExpression\CSharpUseCoalesceExpressionForNullableDiagnosticAnalyzer.cs" />
<Compile Include="UseCoalesceExpression\CSharpUseCoalesceExpressionDiagnosticAnalyzer.cs" />
<Compile Include="UseExpressionBody\Accessors\UseExpressionBodyForAccessorsCodeFixProvider.cs" />
......
// 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.Composition;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.ConvertToInterpolatedString;
namespace Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertToInterpolatedString), Shared]
internal class CSharpConvertConcatenationToInterpolatedStringRefactoringProvider :
AbstractConvertConcatenationToInterpolatedStringRefactoringProvider
{
protected override SyntaxToken CreateInterpolatedStringStartToken(bool isVerbatim)
=> SyntaxFactory.Token(isVerbatim
? SyntaxKind.InterpolatedVerbatimStringStartToken
: SyntaxKind.InterpolatedStringStartToken);
protected override SyntaxToken CreateInterpolatedStringEndToken()
=> SyntaxFactory.Token(SyntaxKind.InterpolatedStringEndToken);
protected override string GetTextWithoutQuotes(string text, bool isVerbatim)
=> isVerbatim
? text.Substring("@'".Length, text.Length - "@''".Length)
: text.Substring("'".Length, text.Length - "''".Length);
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.ConvertToInterpolatedString
{
/// <summary>
/// Code refactoring that converts expresions of the form: a + b + " str " + d + e
/// into:
/// $"{a + b} str {d}{e}".
/// </summary>
internal abstract class AbstractConvertConcatenationToInterpolatedStringRefactoringProvider : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
// Currently only supported if there is no selection. We could consider relaxing
// this if the selection is of a string concatenation expression.
if (context.Span.Length > 0)
{
return;
}
var cancellationToken = context.CancellationToken;
var document = context.Document;
var position = context.Span.Start;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
// Cursor has to at least be touching a string token.
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (!token.Span.IntersectsWith(position) ||
!syntaxFacts.IsStringLiteral(token))
{
return;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
// The string literal has to at least be contained in a concatenation of some form.
// i.e. "foo" + a or a + "foo". However, those concats could be in larger
// concats as well. Walk to the top of that entire chain.
var literalExpression = token.Parent;
var top = literalExpression;
while (IsStringConcat(syntaxFacts, top.Parent, semanticModel, cancellationToken))
{
top = top.Parent;
}
if (top == literalExpression)
{
// We weren't in a concatenation at all.
return;
}
// Now walk down the concatenation collecting all the pieces that we are
// concatenating.
var pieces = new List<SyntaxNode>();
CollectPiecesDown(syntaxFacts, pieces, top, semanticModel, cancellationToken);
// If the entire expression is just concatenated strings, then don't offer to
// make an interpolated string. The user likely manually split this for
// readability.
if (pieces.All(syntaxFacts.IsStringLiteralExpression))
{
return;
}
// Make sure that all the string tokens we're concatenating are the same type
// of string literal. i.e. if we have an expression like: @" "" " + " \r\n "
// then we don't merge this. We don't want to be munging differnet types of
// escape sequences in these strings, so we only support combining the string
// tokens if they're all teh same type.
var firstStringToken = pieces.First(syntaxFacts.IsStringLiteralExpression).GetFirstToken();
var isVerbatimStringLiteral = syntaxFacts.IsVerbatimStringLiteral(firstStringToken);
if (pieces.Where(syntaxFacts.IsStringLiteralExpression).Any(
lit => isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(lit.GetFirstToken())))
{
return;
}
var interpolatedString = CreateInterpolatedString(document, isVerbatimStringLiteral, pieces);
context.RegisterRefactoring(new MyCodeAction(
c => UpdateDocumentAsync(document, root, top, interpolatedString, c)));
}
private Task<Document> UpdateDocumentAsync(Document document, SyntaxNode root, SyntaxNode top, SyntaxNode interpolatedString, CancellationToken c)
{
var newRoot = root.ReplaceNode(top, interpolatedString);
return Task.FromResult(document.WithSyntaxRoot(newRoot));
}
protected SyntaxNode CreateInterpolatedString(
Document document, bool isVerbatimStringLiteral, List<SyntaxNode> pieces)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var generator = SyntaxGenerator.GetGenerator(document);
var startToken = CreateInterpolatedStringStartToken(isVerbatimStringLiteral)
.WithLeadingTrivia(pieces.First().GetLeadingTrivia());
var endToken = CreateInterpolatedStringEndToken()
.WithTrailingTrivia(pieces.Last().GetTrailingTrivia());
var content = new List<SyntaxNode>();
foreach (var piece in pieces)
{
if (syntaxFacts.IsStringLiteralExpression(piece))
{
var text = piece.GetFirstToken().Text;
var textWithoutQuotes = GetTextWithoutQuotes(text, isVerbatimStringLiteral);
content.Add(generator.InterpolatedStringText(
generator.InterpolatedStringTextToken(textWithoutQuotes)));
}
else
{
content.Add(generator.Interpolation(piece.WithoutTrivia()));
}
}
return generator.InterpolatedStringExpression(startToken, content, endToken);
}
protected abstract string GetTextWithoutQuotes(string text, bool isVerbatimStringLiteral);
protected abstract SyntaxToken CreateInterpolatedStringStartToken(bool isVerbatimStringLiteral);
protected abstract SyntaxToken CreateInterpolatedStringEndToken();
private void CollectPiecesDown(
ISyntaxFactsService syntaxFacts,
List<SyntaxNode> pieces,
SyntaxNode node,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
if (!IsStringConcat(syntaxFacts, node, semanticModel, cancellationToken))
{
pieces.Add(node);
return;
}
SyntaxNode left, right;
syntaxFacts.GetPartsOfBinaryExpression(node, out left, out right);
CollectPiecesDown(syntaxFacts, pieces, left, semanticModel, cancellationToken);
pieces.Add(right);
}
private bool IsStringConcat(
ISyntaxFactsService syntaxFacts, SyntaxNode expression,
SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!syntaxFacts.IsBinaryExpression(expression))
{
return false;
}
var method = semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol as IMethodSymbol;
return method?.MethodKind == MethodKind.BuiltinOperator &&
method.ContainingType.SpecialType == SpecialType.System_String &&
(method.MetadataName == WellKnownMemberNames.AdditionOperatorName ||
method.MetadataName == WellKnownMemberNames.ConcatenateOperatorName);
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Convert_to_interpolated_string, createChangedDocument)
{
}
}
}
}
\ No newline at end of file
......@@ -97,6 +97,7 @@
<Compile Include="CodeStyle\AbstractCodeStyleDiagnosticAnalyzer.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.InstallPackageAndAddImportCodeAction.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.InstallWithPackageManagerCodeAction.cs" />
<Compile Include="ConvertToInterpolatedString\AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="FeaturesResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
......@@ -213,7 +214,7 @@
<Compile Include="CodeRefactorings\CodeRefactoringContextExtensions.cs" />
<Compile Include="CodeRefactorings\CodeRefactoring.cs" />
<Compile Include="CodeRefactorings\CodeRefactoringService.cs" />
<Compile Include="ConvertToInterpolatedString\AbstractConvertPlaceholdToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="ConvertToInterpolatedString\AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\ExtractMethod\AbstractExtractMethodCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\GenerateDefaultConstructors\GenerateDefaultConstructorsCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\GenerateFromMembers\AddConstructorParameters\AddConstructorParametersCodeRefactoringProvider.cs" />
......
......@@ -123,6 +123,7 @@
<Compile Include="CodeFixes\Spellcheck\VisualBasicSpellCheckCodeFixProvider.vb" />
<Compile Include="CodeFixes\Suppression\VisualBasicSuppressionCodeFixProvider.vb" />
<Compile Include="CodeLens\VisualBasicDisplayInfoService.vb" />
<Compile Include="ConvertToInterpolatedString\VisualBasicConvertConcatenationToInterpolatedStringRefactoringProvider.vb" />
<Compile Include="ConvertToInterpolatedString\VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider.vb" />
<Compile Include="CodeRefactorings\EncapsulateField\EncapsulateFieldRefactoringProvider.vb" />
<Compile Include="CodeRefactorings\ExtractInterface\ExtractInterfaceCodeRefactoringProvider.vb" />
......
' 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 System.Composition
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.ConvertToInterpolatedString
Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString
<ExportCodeRefactoringProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeRefactoringProviderNames.ExtractMethod), [Shared]>
Friend Class VisualBasicConvertConcatenationToInterpolatedStringRefactoringProvider
Inherits AbstractConvertConcatenationToInterpolatedStringRefactoringProvider
Protected Overrides Function CreateInterpolatedStringStartToken(isVerbatim As Boolean) As SyntaxToken
Return SyntaxFactory.Token(SyntaxKind.DollarSignDoubleQuoteToken)
End Function
Protected Overrides Function CreateInterpolatedStringEndToken() As SyntaxToken
Return SyntaxFactory.Token(SyntaxKind.DoubleQuoteToken)
End Function
Protected Overrides Function GetTextWithoutQuotes(text As String, isVerbatim As Boolean) As String
Return text.Substring("'".Length, text.Length - "''".Length)
End Function
End Class
End Namespace
......@@ -3624,6 +3624,22 @@ public override SyntaxNode ElementAccessExpression(SyntaxNode expression, IEnume
return SyntaxFactory.ElementAccessExpression(ParenthesizeLeft((ExpressionSyntax)expression), SyntaxFactory.BracketedArgumentList(CreateArguments(arguments)));
}
internal override SyntaxNode InterpolatedStringExpression(SyntaxToken startToken, IEnumerable<SyntaxNode> content, SyntaxToken endToken)
=> SyntaxFactory.InterpolatedStringExpression(startToken, SyntaxFactory.List(content.Cast<InterpolatedStringContentSyntax>()), endToken);
internal override SyntaxNode InterpolatedStringText(SyntaxToken textToken)
=> SyntaxFactory.InterpolatedStringText(textToken);
internal override SyntaxToken InterpolatedStringTextToken(string content)
=> SyntaxFactory.Token(
SyntaxFactory.TriviaList(),
SyntaxKind.InterpolatedStringTextToken,
content, "",
SyntaxFactory.TriviaList());
internal override SyntaxNode Interpolation(SyntaxNode syntaxNode)
=> SyntaxFactory.Interpolation((ExpressionSyntax)syntaxNode);
public override SyntaxNode DefaultExpression(SyntaxNode type)
{
return SyntaxFactory.DefaultExpression((TypeSyntax)type);
......
......@@ -1688,9 +1688,13 @@ public SyntaxList<SyntaxNode> GetContentsOfInterpolatedString(SyntaxNode interpo
}
public bool IsStringLiteral(SyntaxToken token)
{
return token.IsKind(SyntaxKind.StringLiteralToken);
}
=> token.IsKind(SyntaxKind.StringLiteralToken);
public bool IsStringLiteralExpression(SyntaxNode node)
=> node.Kind() == SyntaxKind.StringLiteralExpression;
public bool IsVerbatimStringLiteral(SyntaxToken token)
=> token.IsVerbatimStringLiteral();
public SeparatedSyntaxList<SyntaxNode> GetArgumentsForInvocationExpression(SyntaxNode invocationExpression)
{
......@@ -1874,6 +1878,9 @@ public bool IsPossibleTupleContext(SyntaxTree syntaxTree, int position, Cancella
public bool IsNullLiteralExpression(SyntaxNode node)
=> node.Kind() == SyntaxKind.NullLiteralExpression;
public bool IsBinaryExpression(SyntaxNode node)
=> node is BinaryExpressionSyntax;
public void GetPartsOfBinaryExpression(SyntaxNode node, out SyntaxNode left, out SyntaxNode right)
{
var binaryExpression = (BinaryExpressionSyntax)node;
......
......@@ -1455,6 +1455,12 @@ public SyntaxNode CatchClause(ITypeSymbol type, string identifier, IEnumerable<S
#endregion
#region Expressions
internal abstract SyntaxToken InterpolatedStringTextToken(string content);
internal abstract SyntaxNode InterpolatedStringText(SyntaxToken textToken);
internal abstract SyntaxNode Interpolation(SyntaxNode syntaxNode);
internal abstract SyntaxNode InterpolatedStringExpression(SyntaxToken startToken, IEnumerable<SyntaxNode> content, SyntaxToken endToken);
/// <summary>
/// An expression that represents the default value of a type.
/// This is typically a null value for reference types or a zero-filled value for value types.
......
......@@ -28,7 +28,10 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsHashToken(SyntaxToken token);
bool IsLiteral(SyntaxToken token);
bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token);
bool IsStringLiteral(SyntaxToken token);
bool IsStringLiteralExpression(SyntaxNode node);
bool IsVerbatimStringLiteral(SyntaxToken token);
bool IsTypeNamedVarInVariableOrFieldDeclaration(SyntaxToken token, SyntaxNode parent);
bool IsTypeNamedDynamic(SyntaxToken token, SyntaxNode parent);
......@@ -41,7 +44,6 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsNullLiteralExpression(SyntaxNode node);
string GetText(int kind);
bool IsInInactiveRegion(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken);
bool IsInNonUserCode(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken);
bool IsEntirelyWithinStringOrCharOrNumericLiteral(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken);
......@@ -55,6 +57,7 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsObjectCreationExpression(SyntaxNode node);
SyntaxNode GetObjectCreationInitializer(SyntaxNode objectCreationExpression);
bool IsBinaryExpression(SyntaxNode node);
void GetPartsOfBinaryExpression(SyntaxNode node, out SyntaxNode left, out SyntaxNode right);
void GetPartsOfConditionalExpression(SyntaxNode node, out SyntaxNode condition, out SyntaxNode whenTrue, out SyntaxNode whenFalse);
......
......@@ -84,6 +84,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Return ExpressionGenerator.GenerateExpression(value)
End Function
Friend Overrides Function InterpolatedStringExpression(startToken As SyntaxToken, content As IEnumerable(Of SyntaxNode), endToken As SyntaxToken) As SyntaxNode
Return SyntaxFactory.InterpolatedStringExpression(
startToken, SyntaxFactory.List(content.Cast(Of InterpolatedStringContentSyntax)), endToken)
End Function
Friend Overrides Function InterpolatedStringText(textToken As SyntaxToken) As SyntaxNode
Return SyntaxFactory.InterpolatedStringText(textToken)
End Function
Friend Overrides Function InterpolatedStringTextToken(content As String) As SyntaxToken
Return SyntaxFactory.InterpolatedStringTextToken(content, "")
End Function
Friend Overrides Function Interpolation(syntaxNode As SyntaxNode) As SyntaxNode
Return SyntaxFactory.Interpolation(DirectCast(syntaxNode, ExpressionSyntax))
End Function
Public Overrides Function DefaultExpression(type As ITypeSymbol) As SyntaxNode
Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword))
End Function
......
......@@ -1364,6 +1364,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return token.IsKind(SyntaxKind.StringLiteralToken)
End Function
Public Function IsStringLiteralExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsStringLiteralExpression
Return node.Kind() = SyntaxKind.StringLiteralExpression
End Function
Public Function IsVerbatimStringLiteral(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsVerbatimStringLiteral
' VB does not have verbatim strings
Return False
End Function
Public Function GetArgumentsForInvocationExpression(invocationExpression As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) Implements ISyntaxFactsService.GetArgumentsForInvocationExpression
Dim arguments = TryCast(invocationExpression, InvocationExpressionSyntax)?.ArgumentList?.Arguments
Return If(arguments.HasValue, arguments.Value, Nothing)
......@@ -1551,6 +1560,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return node.Kind() = SyntaxKind.NothingLiteralExpression
End Function
Public Function IsBinaryExpression(node As SyntaxNode) As Boolean Implements ISyntaxFactsService.IsBinaryExpression
Return TypeOf node Is BinaryExpressionSyntax
End Function
Public Sub GetPartsOfBinaryExpression(node As SyntaxNode, ByRef left As SyntaxNode, ByRef right As SyntaxNode) Implements ISyntaxFactsService.GetPartsOfBinaryExpression
Dim binaryExpression = DirectCast(node, BinaryExpressionSyntax)
left = binaryExpression.Left
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册