File: Wrapping\SeparatedSyntaxList\CSharpArgumentWrapper.cs
Web Access
Project: src\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.
 
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Wrapping;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList;
 
internal sealed partial class CSharpArgumentWrapper
    : AbstractCSharpSeparatedSyntaxListWrapper<BaseArgumentListSyntax, ArgumentSyntax>
{
    protected override string Align_wrapped_items => FeaturesResources.Align_wrapped_arguments;
    protected override string Indent_all_items => FeaturesResources.Indent_all_arguments;
    protected override string Indent_wrapped_items => FeaturesResources.Indent_wrapped_arguments;
    protected override string Unwrap_all_items => FeaturesResources.Unwrap_all_arguments;
    protected override string Unwrap_and_indent_all_items => FeaturesResources.Unwrap_and_indent_all_arguments;
    protected override string Unwrap_list => FeaturesResources.Unwrap_argument_list;
    protected override string Wrap_every_item => FeaturesResources.Wrap_every_argument;
    protected override string Wrap_long_list => FeaturesResources.Wrap_long_argument_list;
 
    public override bool Supports_UnwrapGroup_WrapFirst_IndentRest => true;
    public override bool Supports_WrapEveryGroup_UnwrapFirst => true;
    public override bool Supports_WrapLongGroup_UnwrapFirst => true;
 
    protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options)
        => false;
 
    protected override bool ShouldMoveCloseBraceToNewLine
        => false;
 
    protected override SyntaxToken FirstToken(BaseArgumentListSyntax listSyntax)
        => listSyntax.GetOpenToken();
 
    protected override SyntaxToken LastToken(BaseArgumentListSyntax listSyntax)
        => listSyntax.GetCloseToken();
 
    protected override SeparatedSyntaxList<ArgumentSyntax> GetListItems(BaseArgumentListSyntax listSyntax)
        => listSyntax.Arguments;
 
    protected override BaseArgumentListSyntax? TryGetApplicableList(SyntaxNode node)
        => node switch
        {
            InvocationExpressionSyntax invocationExpression => invocationExpression.ArgumentList,
            ElementAccessExpressionSyntax elementAccessExpression => elementAccessExpression.ArgumentList,
            BaseObjectCreationExpressionSyntax objectCreationExpression => objectCreationExpression.ArgumentList,
            ConstructorInitializerSyntax constructorInitializer => constructorInitializer.ArgumentList,
            _ => null,
        };
 
    protected override bool PositionIsApplicable(
        SyntaxNode root,
        int position,
        SyntaxNode declaration,
        bool containsSyntaxError,
        BaseArgumentListSyntax listSyntax)
    {
        if (containsSyntaxError)
            return false;
 
        var startToken = listSyntax.GetFirstToken();
 
        if (declaration is InvocationExpressionSyntax or ElementAccessExpressionSyntax)
        {
            // If we have something like  Foo(...)  or  this.Foo(...)  allow anywhere in the Foo(...)
            // section.
            var expr = (declaration as InvocationExpressionSyntax)?.Expression ??
                       ((ElementAccessExpressionSyntax)declaration).Expression;
            var name = TryGetInvokedName(expr);
 
            startToken = name == null ? listSyntax.GetFirstToken() : name.GetFirstToken();
        }
        else if (declaration is BaseObjectCreationExpressionSyntax)
        {
            // allow anywhere in `new Foo(...)`
            startToken = declaration.GetFirstToken();
        }
        else if (declaration is ConstructorInitializerSyntax constructorInitializer)
        {
            // allow anywhere in `this(...)` or `base(...)`
            startToken = constructorInitializer.ThisOrBaseKeyword;
        }
 
        var endToken = listSyntax.GetLastToken();
        var span = TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End);
        if (!span.IntersectsWith(position))
            return false;
 
        // allow anywhere in the arg list, as long we don't end up walking through something
        // complex like a lambda/anonymous function.
        var token = root.FindToken(position);
        if (token.GetRequiredParent().Ancestors().Contains(listSyntax))
        {
            for (var current = token.Parent; current != listSyntax; current = current?.Parent)
            {
                if (CSharpSyntaxFacts.Instance.IsAnonymousFunctionExpression(current))
                    return false;
            }
        }
 
        return true;
    }
 
    private static ExpressionSyntax? TryGetInvokedName(ExpressionSyntax expr)
    {
        // `Foo(...)`.  Allow up through the 'Foo' portion
        if (expr is NameSyntax name)
            return name;
 
        // `this[...]`. Allow up through the 'this' token.
        if (expr is ThisExpressionSyntax or BaseExpressionSyntax)
            return expr;
 
        // expr.Foo(...) or expr?.Foo(...)
        // All up through the 'Foo' portion.
        //
        // Otherwise, only allow in the arg list.
        return (expr as MemberAccessExpressionSyntax)?.Name ??
               (expr as MemberBindingExpressionSyntax)?.Name;
    }
}