| File: CodeRefactorings\NodeSelectionHelpers.cs | Web Access |
| Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features) |
// 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. #nullable disable using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings; internal static class NodeSelectionHelpers { internal static async Task<ImmutableArray<SyntaxNode>> GetSelectedDeclarationsOrVariablesAsync(CodeRefactoringContext context) { var (document, span, cancellationToken) = context; if (span.IsEmpty) { // if the span is empty then we are only selecting one "member" (which could include a field which declared multiple actual members) // Consider: // MemberDeclaration: member that can be declared in type (those are the ones we can pull up) // VariableDeclaratorSyntax: for fields the MemberDeclaration can actually represent multiple declarations, e.g. `int a = 0, b = 1;`. // ..Since the user might want to select & pull up only one of them (e.g. `int a = 0, [|b = 1|];` we also look for closest VariableDeclaratorSyntax. var memberDeclaration = await context.TryGetRelevantNodeAsync<MemberDeclarationSyntax>().ConfigureAwait(false); if (memberDeclaration == null) { // could not find a member, we may be directly on a variable declaration var varDeclarator = await context.TryGetRelevantNodeAsync<VariableDeclaratorSyntax>().ConfigureAwait(false); return varDeclarator == null ? [] : [varDeclarator]; } else { return memberDeclaration switch { FieldDeclarationSyntax fieldDeclaration => fieldDeclaration.Declaration.Variables.AsImmutable<SyntaxNode>(), EventFieldDeclarationSyntax eventFieldDeclaration => eventFieldDeclaration.Declaration.Variables.AsImmutable<SyntaxNode>(), IncompleteMemberSyntax or GlobalStatementSyntax => [], _ => [memberDeclaration], }; } } else { // if the span is non-empty, then we get potentially multiple members // Note: even though this method handles the empty span case, we don't use it because it doesn't correctly // pick up on keywords before the declaration, such as "public static int". // We could potentially use it for every case if that behavior changes var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var members = await CSharpSelectedMembers.Instance.GetSelectedMembersAsync(tree, span, allowPartialSelection: true, cancellationToken).ConfigureAwait(false); // if we get a node that would not have an obtainable symbol (such as the ones below) // we return an empty list instead of filtering so we don't get other potentially // malformed syntax nodes. // Consider pub[||] static int Foo; // Which has 2 member nodes (an incomplete and a field), but we'd only expect one return members.Any(m => m is GlobalStatementSyntax or IncompleteMemberSyntax) ? [] : members; } } }