|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings;
[ExportLanguageService(typeof(IRefactoringHelpersService), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpRefactoringHelpersService() : AbstractRefactoringHelpersService<ExpressionSyntax, ArgumentSyntax, ExpressionStatementSyntax>
{
protected override IHeaderFacts HeaderFacts => CSharpHeaderFacts.Instance;
public override bool IsBetweenTypeMembers(SourceText sourceText, SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? typeDeclaration)
{
var token = root.FindToken(position);
var typeDecl = token.GetAncestor<TypeDeclarationSyntax>();
typeDeclaration = typeDecl;
if (typeDecl == null)
return false;
RoslynDebug.AssertNotNull(typeDeclaration);
if (position < typeDecl.OpenBraceToken.Span.End ||
position > typeDecl.CloseBraceToken.Span.Start)
{
return false;
}
var line = sourceText.Lines.GetLineFromPosition(position);
if (!line.IsEmptyOrWhitespace())
return false;
var member = typeDecl.Members.FirstOrDefault(d => d.FullSpan.Contains(position));
if (member == null)
{
// There are no members, or we're after the last member.
return true;
}
else
{
// We're within a member. Make sure we're in the leading whitespace of
// the member.
if (position < member.SpanStart)
{
foreach (var trivia in member.GetLeadingTrivia())
{
if (!trivia.IsWhitespaceOrEndOfLine())
return false;
if (trivia.FullSpan.Contains(position))
return true;
}
}
}
return false;
}
protected override IEnumerable<SyntaxNode> ExtractNodesSimple(SyntaxNode? node, ISyntaxFactsService syntaxFacts)
{
if (node == null)
{
yield break;
}
foreach (var extractedNode in base.ExtractNodesSimple(node, syntaxFacts))
{
yield return extractedNode;
}
// `var a = b;`
// -> `var a = b`;
if (node is LocalDeclarationStatementSyntax localDeclaration)
{
yield return localDeclaration.Declaration;
}
// var `a = b`;
if (node is VariableDeclaratorSyntax declarator)
{
var declaration = declarator.Parent;
if (declaration?.Parent is LocalDeclarationStatementSyntax localDeclarationStatement)
{
var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(localDeclarationStatement);
if (variables.Count == 1)
{
// -> `var a = b`;
yield return declaration;
// -> `var a = b;`
yield return localDeclarationStatement;
}
}
}
}
}
|