提交 08f74cf5 编写于 作者: P Petr Houška

Rework NodeExtractor to be language agnostic via ISyntaxFactsService.

上级 bc74f96f
......@@ -9,36 +9,5 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings
[ExportLanguageService(typeof(IRefactoringHelpersService), LanguageNames.CSharp), Shared]
internal class CSharpRefactoringHelpersService : AbstractRefactoringHelpersService
{
public override SyntaxNode DefaultNodeExtractor(SyntaxNode node)
{
switch (node)
{
case LocalDeclarationStatementSyntax localDeclaration:
{
if (localDeclaration.Declaration.Variables.Count == 1 && localDeclaration.Declaration.Variables.First().Initializer != null)
{
var initializer = localDeclaration.Declaration.Variables.First().Initializer;
return initializer.Value;
}
break;
}
case ExpressionStatementSyntax expressionStatement:
{
if (expressionStatement.Expression is AssignmentExpressionSyntax assignmentExpression)
{
if (assignmentExpression.Right != null)
{
return assignmentExpression.Right;
}
}
break;
}
}
return node;
}
}
}
// 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.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeRefactorings
{
internal abstract class AbstractRefactoringHelpersService : IRefactoringHelpersService
{
public abstract SyntaxNode DefaultNodeExtractor(SyntaxNode node);
public async Task<TSyntaxNode> TryGetSelectedNodeAsync<TSyntaxNode>(
Document document, TextSpan selection, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode
{
return await TryGetSelectedNodeAsync(document, selection, n => n is TSyntaxNode, DefaultNodeExtractor, cancellationToken).ConfigureAwait(false) as TSyntaxNode;
return await TryGetSelectedNodeAsync(document, selection, n => n is TSyntaxNode, cancellationToken).ConfigureAwait(false) as TSyntaxNode;
}
public Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan selection, Predicate<SyntaxNode> predicate, CancellationToken cancellationToken)
......@@ -25,8 +22,9 @@ public Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan sele
return TryGetSelectedNodeAsync(document, selection, predicate, DefaultNodeExtractor, cancellationToken);
}
public async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan selection, Predicate<SyntaxNode> predicate, Func<SyntaxNode, SyntaxNode> extractNode, CancellationToken cancellationToken)
public async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan selection, Predicate<SyntaxNode> predicate, Func<SyntaxNode, ISyntaxFactsService, SyntaxNode> extractNode, CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var selectionStripped = await CodeRefactoringHelpers.GetStrippedTextSpan(document, selection, cancellationToken).ConfigureAwait(false);
......@@ -38,7 +36,7 @@ public async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpa
SyntaxNode prevNode;
do
{
var wantedNode = TryGetAcceptedNodeOrExtracted(node, predicate, extractNode);
var wantedNode = TryGetAcceptedNodeOrExtracted(node, predicate, extractNode, syntaxFacts);
if (wantedNode != default)
{
return wantedNode;
......@@ -70,7 +68,7 @@ public async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpa
do
{
// consider either a Node that is a parent of touched Token (selection can be within) or ancestor Node of such Token whose span starts on selection
var wantedNode = TryGetAcceptedNodeOrExtracted(rightNode, predicate, extractNode);
var wantedNode = TryGetAcceptedNodeOrExtracted(rightNode, predicate, extractNode, syntaxFacts);
if (wantedNode != default)
{
return wantedNode;
......@@ -102,7 +100,7 @@ public async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpa
do
{
// consider either a Node that is a parent of touched Token (selection can be within) or ancestor Node of such Token whose span ends on selection
var wantedNode = TryGetAcceptedNodeOrExtracted(leftNode, predicate, extractNode);
var wantedNode = TryGetAcceptedNodeOrExtracted(leftNode, predicate, extractNode, syntaxFacts);
if (wantedNode != default)
{
return wantedNode;
......@@ -116,7 +114,7 @@ public async Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpa
// nothing found
return default;
static SyntaxNode TryGetAcceptedNodeOrExtracted(SyntaxNode node, Predicate<SyntaxNode> predicate, Func<SyntaxNode, SyntaxNode> extractNode)
static SyntaxNode TryGetAcceptedNodeOrExtracted(SyntaxNode node, Predicate<SyntaxNode> predicate, Func<SyntaxNode, ISyntaxFactsService, SyntaxNode> extractNode, ISyntaxFactsService syntaxFacts)
{
if (node == default)
{
......@@ -128,7 +126,7 @@ static SyntaxNode TryGetAcceptedNodeOrExtracted(SyntaxNode node, Predicate<Synta
return node;
}
var extrNode = extractNode(node);
var extrNode = extractNode(node, syntaxFacts);
if (extrNode != default && predicate(extrNode))
{
return extrNode;
......@@ -137,5 +135,42 @@ static SyntaxNode TryGetAcceptedNodeOrExtracted(SyntaxNode node, Predicate<Synta
return default;
}
}
public virtual SyntaxNode DefaultNodeExtractor(SyntaxNode node, ISyntaxFactsService syntaxFacts)
{
// var a = b;
// -> b
if (syntaxFacts.IsLocalDeclarationStatement(node))
{
var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(node);
if (variables.Count == 1)
{
var declaredVariable = variables.First();
var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(declaredVariable);
if (initializer != default)
{
var value = syntaxFacts.GetValueOfEqualsValueClause(initializer);
if (value != default)
{
return value;
}
}
}
}
// a = b;
// -> b
if (syntaxFacts.IsSimpleAssignmentStatement(node))
{
syntaxFacts.GetPartsOfAssignmentExpressionOrStatement(node, out _, out _, out var rightSide);
if (rightSide != default)
{
return rightSide;
}
}
return node;
}
}
}
......@@ -4,6 +4,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CodeRefactorings
......@@ -27,7 +28,7 @@ internal interface IRefactoringHelpersService : ILanguageService
/// that are under those that might be selected / considered (as described above). It is a <see cref="Func{SyntaxNode, SyntaxNode}"/> that
/// should always return either given Node or a Node somewhere below it that should be tested with <paramref name="predicate"/> and
/// potentially returned instead of current Node.
/// E.g. <see cref="DefaultNodeExtractor{TNode}(SyntaxNode)"/>
/// E.g. <see cref="DefaultNodeExtractor(SyntaxNode, ISyntaxFactsService)"/>
/// allows returning right side Expresion node even if whole AssignmentNode is selected.
/// </para>
/// <para>
......@@ -36,7 +37,7 @@ internal interface IRefactoringHelpersService : ILanguageService
/// of tokens gracefully.
/// </para>
/// </summary>
Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan selection, Predicate<SyntaxNode> predicate, Func<SyntaxNode, SyntaxNode> extractNode, CancellationToken cancellationToken);
Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan selection, Predicate<SyntaxNode> predicate, Func<SyntaxNode, ISyntaxFactsService, SyntaxNode> extractNode, CancellationToken cancellationToken);
/// <summary>
/// <para>
......@@ -87,9 +88,9 @@ internal interface IRefactoringHelpersService : ILanguageService
Task<SyntaxNode> TryGetSelectedNodeAsync(Document document, TextSpan selection, Predicate<SyntaxNode> predicate, CancellationToken cancellationToken);
/// <summary>
/// Extractor function for <see cref="TryGetSelectedNodeAsync(Document, TextSpan, Predicate{SyntaxNode}, Func{SyntaxNode, SyntaxNode}, CancellationToken)"/> methods that retrieves expressions from
/// Extractor function for <see cref="TryGetSelectedNodeAsync(Document, TextSpan, Predicate{SyntaxNode}, Func{SyntaxNode, ISyntaxFactsService, SyntaxNode}, CancellationToken)"/> methods that retrieves expressions from
/// declarations and assignments. Otherwise returns unchanged <paramref name="node"/>.
/// </summary>
SyntaxNode DefaultNodeExtractor(SyntaxNode node);
SyntaxNode DefaultNodeExtractor(SyntaxNode node, ISyntaxFactsService syntaxFacts);
}
}
......@@ -9,24 +9,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings
<ExportLanguageService(GetType(IRefactoringHelpersService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicRefactoringHelpersService
Inherits AbstractRefactoringHelpersService
Public Overrides Function DefaultNodeExtractor(node As SyntaxNode) As SyntaxNode
If TypeOf node Is LocalDeclarationStatementSyntax Then
Dim localDeclaration = CType(node, LocalDeclarationStatementSyntax)
If localDeclaration.Declarators.Count = 1 And localDeclaration.Declarators.First.Initializer IsNot Nothing Then
Dim initilizer = localDeclaration.Declarators.First.Initializer
If initilizer IsNot Nothing Then
Return initilizer
End If
End If
ElseIf TypeOf node Is AssignmentStatementSyntax Then
Dim assignmentStatement = CType(node, AssignmentStatementSyntax)
If assignmentStatement.Right IsNot Nothing Then
Return assignmentStatement.Right
End If
End If
Return node
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册