首页 > 其他分享 >Roslyn+T4+EnvDTE项目完全自动化(8) ——转换linq表达式

Roslyn+T4+EnvDTE项目完全自动化(8) ——转换linq表达式

时间:2022-10-14 16:48:58浏览次数:81  
标签:used name buf T4 EnvDTE syntax Roslyn var expression

写代码最开始时,为了方便大多写linq method chain,随着业务发展,需要把linq method chain转换成LINQ-expression更方便。

resharper有下面3种,可以重构项目:

但没有转换成LINQ-expression的插件。我写了一个插件可以转换成LINQ-expression。

 

代码:

    [ExportMenuItem("Refactor/linq")]
    public class ConvertLinqExpression : MenuItemAction
    {
        public ConvertLinqExpression()
            : base("convert linq expression")
        {
        }
        public override async Task Execute()
        {
            var dte = Helpers.MainViewModel.VsViewModel.SelectedItem.DTE;
            var documentSyntaxNode = new DocumentSyntaxNode(dte);
            ExpressionSyntax listExpression = null;
            var array = documentSyntaxNode.Node.Root.GetParentsFromTextSpan<InvocationExpressionSyntax>(documentSyntaxNode.Span,
                node =>
                {
                    var kinds = new[]
                    {
                        SyntaxKind.Interpolation,
                        SyntaxKind.ExpressionStatement,
                    };
                    return !kinds.Contains(node.Kind());
                }).ToArray();
            if (array.Length == 0)
            {
                if (JustConvert(documentSyntaxNode)) return;
                throw new Exception("没找到 Linq method");
            }
            var buf = new List<(string name, InvocationExpressionSyntax syntax)>();
            var names = new List<string>()
            {
                nameof(Enumerable.Where),
                nameof(Enumerable.Select),

                nameof(Enumerable.SelectMany),
                nameof(Enumerable.GroupBy),
                nameof(Enumerable.OrderBy),
                nameof(Enumerable.OrderByDescending),
            };
            var firstNames = new[]
            {
                nameof(Enumerable.First),
                nameof(Enumerable.FirstOrDefault),
            };
            names.AddRange(firstNames);

            for (var i = 0; i < array.Length; i++)
            {
                var syntax = array[i];
                if (syntax.Expression is MemberAccessExpressionSyntax member)
                {
                    var name = member.Name.Identifier.ValueText;
                    if (listExpression == null && names.Contains(name))
                    {
                        listExpression = member.Expression;
                    }
                    if (!names.Contains(name) || syntax.ArgumentList.Arguments.Count != 1)
                    {
                        break;
                    }

                    buf.Add((name, syntax));
                    if (firstNames.Contains(name))
                    {
                        break;
                    }
                }
            }

            if (listExpression == null || buf.Count == 0)
            {
                if (JustConvert(documentSyntaxNode)) return;
                throw new Exception($@"没找到 Linq method chain:
{array.JoinNewLine()}");
            }

            var used = new List<string>();

            var sb = new StringBuilder();
            var space = GetSameLineSpace(listExpression);

            for (var i = 0; i < buf.Count; i++)
            {
                var syntax = buf[i];
                var argument = syntax.syntax.ArgumentList.Arguments.First();
                if (used.Count == 0)
                {
                    TryAdd(used, syntax.syntax.GetLambdaArgumentName() ?? "p");
                }

                if (i == 0)
                {
                    sb.AppendLine($"{space}from {used.Last()} in {listExpression}");
                }

                switch (syntax.name)
                {
                    case nameof(Enumerable.Select):
                        {
                            var expression = argument.Expression;
                            var value = expression is SimpleLambdaExpressionSyntax lambda
                                        ? $"{TryRename(lambda, used.LastOrDefault())}"
                                        : $"{expression}({used.Last()})";

                            if (i == buf.Count - 1)
                            {
                                sb.AppendLine($"{space}select {value}");
                            }
                            else
                            {
                                sb.AppendLine($"{space}let {GetNewParam(used, buf, i)} = {value}");
                            }
                            break;
                        }
                    case nameof(Enumerable.First):
                    case nameof(Enumerable.FirstOrDefault):
                    case nameof(Enumerable.Where):
                        {
                            var expression = argument.Expression;
                            var value = expression is SimpleLambdaExpressionSyntax lambda
                                ? $"{TryRename(lambda, used.LastOrDefault())}"
                                : $"{expression}({used.Last()})";

                            sb.AppendLine($"{space}where {value}");
                            if (i == buf.Count - 1)
                            {
                                sb.AppendLine($"{space}select {used.Last()}");
                            }
                            break;
                        }
                    case nameof(Enumerable.SelectMany):
                        {
                            var expression = argument.Expression;
                            switch (expression)
                            {
                                case SimpleLambdaExpressionSyntax lambda:
                                    sb.AppendLine($"{space}from {GetNewParam(used, buf, i)} in {TryRename(lambda, TryGetLast2Name(used))}");
                                    break;
                                default:
                                    sb.AppendLine($"{space}from {GetNewParam(used, buf, i)} in {expression}({TryGetLast2Name(used)})");
                                    break;
                            }

                            break;
                        }
                    case nameof(Enumerable.GroupBy):
                        {
                            var expression = argument.Expression;
                            switch (expression)
                            {
                                case SimpleLambdaExpressionSyntax lambda:
                                    if (i == buf.Count - 1)
                                    {
                                        sb.AppendLine($"{space}group {used.Last()} by {TryRename(lambda, used.LastOrDefault())}");
                                    }
                                    else
                                    {
                                        sb.AppendLine($"{space}group {used.Last()} by {TryRename(lambda, used.LastOrDefault())} into {GetNewParam(used, buf, i)}");
                                        //sb.AppendLine($"from {GetNewParam(used, buf, i)} in g");
                                    }
                                    break;
                                default:
                                    if (i == buf.Count - 1)
                                    {
                                        sb.AppendLine($"{space}group {used.Last()} by {expression}({used.Last()})");
                                    }
                                    else
                                    {
                                        sb.AppendLine($"{space}group {used.Last()} by {expression}({used.Last()}) into {GetNewParam(used, buf, i)}");
                                    }
                                    break;
                            }

                            break;
                        }
                    case nameof(Enumerable.OrderByDescending):
                        {
                            var expression = argument.Expression;
                            switch (expression)
                            {
                                case SimpleLambdaExpressionSyntax lambda:
                                    sb.AppendLine($"{space}orderby {TryRename(lambda, used.LastOrDefault())} descending");
                                    break;
                                default:
                                    sb.AppendLine($"{space}orderby {expression}({used.Last()}) descending");
                                    break;
                            }

                            break;
                        }
                    case nameof(Enumerable.OrderBy):
                        {
                            var expression = argument.Expression;
                            switch (expression)
                            {
                                case SimpleLambdaExpressionSyntax lambda:
                                    sb.AppendLine($"{space}orderby {TryRename(lambda, used.LastOrDefault())}");
                                    break;
                                default:
                                    sb.AppendLine($"{space}orderby {expression}({used.Last()})");
                                    break;
                            }

                            break;
                        }
                }
                if (i == buf.Count - 1
                    && syntax.name != nameof(Enumerable.Select)
                    && syntax.name != nameof(Enumerable.Where)
                    && !firstNames.Contains(syntax.name)
                    )
                {
                    sb.AppendLine($"{space}select {used.Last()}");
                }
            }

            var srcSyntax = buf.Last().syntax;

            var code = sb.TrimNewLine();
            var end = firstNames.Contains(buf.Last().name) ? $".{buf.Last().name}()" : null;
            var text = $@"(
{code}
{space}){end}";
            documentSyntaxNode.Save(documentSyntaxNode.Node.Root.GetText().InsertText(new TextChange(srcSyntax.Span, text)));
        }

        private static string TryGetLast2Name(List<string> used)
        {
            return used.Count < 2 ? used.LastOrDefault() : used[used.Count - 2];
        }

        private bool JustConvert(DocumentSyntaxNode documentSyntaxNode)
        {
            var syntax = (
                from p in documentSyntaxNode.Node.Root.DescendantNodes().OfType<ExpressionSyntax>()
                where p.Span.IntersectsWith(documentSyntaxNode.Span)
                let a = p is NameSyntax
                orderby a descending, p.SpanStart descending
                select p
                )
                .FirstOrDefault();
            if (syntax == null) return false;
            if (syntax.GetParent(false, null) is MemberAccessExpressionSyntax node && node.Name == syntax)
            {
                JustConvert(documentSyntaxNode, node.GetParentNearest());
            }
            else
            {
                JustConvert(documentSyntaxNode, syntax.GetParentNearest());
            }
            return true;

        }

        private void JustConvert(DocumentSyntaxNode documentSyntaxNode, SyntaxNode syntaxNode)
        {
            var s = GetSameLineSpace(syntaxNode);
            var v = $@"(
{s}from p in {syntaxNode}
{s}select p
{s})";
            documentSyntaxNode.Save(documentSyntaxNode.Node.Root.GetText()
                .InsertText(new TextChange(syntaxNode.Span, v)));
        }
        private void JustConvert(DocumentSyntaxNode documentSyntaxNode, string methodName, TextSpan textSpan,
            SyntaxNode syntaxNode,
            string whereCode)
        {
            var s = GetSameLineSpace(syntaxNode);
            string w = null;
            if (whereCode != null)
            {
                w = $@"
{s}where {whereCode}";
            }
            var v = $@"(
{s}from p in {syntaxNode}{w}
{s}select p
{s}).{methodName}()";
            documentSyntaxNode.Save(documentSyntaxNode.Node.Root.GetText()
                .InsertText(new TextChange(textSpan, v)));
        }

        private string GetSameLineSpace(SyntaxNode syntax)
        {
            var line = syntax.GetLocation().GetLineSpan().StartLinePosition.Line;
            SyntaxNode sameLine = null;
            foreach (var syntaxNode in syntax.GetParents<SyntaxNode>())
            {
                if (syntaxNode.GetLocation().GetLineSpan().StartLinePosition.Line == line)
                {
                    sameLine = syntaxNode;
                }
                else
                {
                    break;
                }
            }

            var trivia = sameLine?.GetLeadingTrivia().FirstOrDefault(p => p.IsKind(SyntaxKind.WhitespaceTrivia))
                .ToFullString();
            if (string.IsNullOrEmpty(trivia))
            {
                return new string('\t', 3);
            }
            return trivia + new string('\t', 1);
        }

        private string TryRename(SimpleLambdaExpressionSyntax lambda, string usedName)
        {
            var name = lambda.GetLambdaArgumentName();
            var body = lambda.ExpressionBody ?? throw new Exception("没有简单LambdaExpression");
            if (string.IsNullOrEmpty(usedName) || usedName == name)
            {
                return body.ToFullString();
            }
            var regex = $@"\b{Regex.Escape(name)}\b".GetRegex(true, false);
            return regex.Replace(body.ToFullString(), usedName);
        }

        private string GetNewParam(List<string> used, List<(string name, InvocationExpressionSyntax syntax)> buf, int start)
        {
            if (start < buf.Count - 1)
            {
                var argumentName = buf[start + 1].syntax.GetLambdaArgumentName();
                if (string.IsNullOrEmpty(argumentName) || used.Contains(argumentName))
                {
                    goto New;
                }

                TryAdd(used, argumentName);
                return argumentName;
            }
            New:
            for (int i = 0; ; i++)
            {
                var name = $"p{(i == 0 ? "" : i)}";
                if (TryAdd(used, name))
                {
                    return name;
                }
            }
        }

        private static bool TryAdd(List<string> used, string name)
        {
            if (!used.Contains(name))
            {
                used.Add(name);
                return true;
            }
            return false;
        }
    }

  

标签:used,name,buf,T4,EnvDTE,syntax,Roslyn,var,expression
From: https://www.cnblogs.com/metoget/p/16792031.html

相关文章

  • UOJ pjudge LOJ 试题乱做 Part4
    概率太尼玛有意思了,啊哈哈哈.\(Alex\_Wei\)唱歌好听!\(\text{【LOJ\#2834】「JOISC2018Day2」修行}\)\(\color{red}{\text{[HARD]}}\)概率真好van,这个世界真......
  • MT4 serverAPI开发接口(头文件)
    如有疑问请联系v:yunkeji5868同managerAPI接口一样,下面是serverAPI提供的接口,可以保存为.h文件直接导入到自己工程中使用。具体如果使用可看我的其他文章。 //+-------......
  • 文件系统EXT3,EXT4和XFS的区别
    文件系统EXT3,EXT4和XFS的区别:1.EXT3(1)最多只能支持32TB的文件系统和2TB的文件,实际只能容纳2TB的文件系统和16GB的文件(2)Ext3目前只支持32000个子目录(3)Ext3文件系统使用32位......
  • h5:vue3 + ts + vite + vuex + axios + vant4 + scss + postcss+mockjs+element-plus
    模板地址:https://gitee.com/zhang_meng_lei/mobile-template-h5-vue3/tree/master安装element-plus:yarnaddelement-plus(目前已导入但未实现代码)按需导入:https://el......
  • 关于今天T4的复杂度证明
    关于今天T4复杂度证明的人话翻译:(我是只会搞T4了吗)首先我们略去前面的所有东西,直接到证明这个贪心处理之后的向量个数在\(O(n^{\frac23})\)级别:考察我们之前选择向量的......
  • T4 凹函数 整理
    MD凹函数题解题意是多组数据,给定\(n,m\),求定义域和值域分别为\([0,n],[0,m]\)的单调凹函数至多经过几个整点考虑相邻两个经过整点的差,原问题等价于选出k个二维向量\((......
  • 2022-J T4 小熊的果篮(未完)
    题目链接嗯你怎么知道我还没做出来正解  这个题暴力可以拿70分每次记录一下拿出即可(一定要注意不能零一存,因为本次也要算入判断过程)(所以我们可以更新的时候更新......
  • Servlet4.0 Response
    Servlet4.0Response对象Response对象封装Server返回Client的所有信息。在HTTP协议中,Server传达给Client信息转换到HTTPHeader或者HTTPBODY中。5.1Buffering缓冲区Serve......
  • 764-GSPRINT4502 2MP-4.5微米 全局快门 高速 CMOS 图像传感器
    GSPRINT45022MP-4.5微米全局快门高速CMOS图像传感器      GSPRINT4502是一款一千万分辨率(2048x1216),2/3”光学尺寸的高速图像传感器,......
  • Atcoder试题乱做 Part4
    时光怎不经一生浮浮沉沉已半生一壶浊酒欲随风一步一瞥似惊鸿情字要如何追问一指兰花为谁挽留\(\text{[ARC147D]SetsScores}\)\(\color{green}{\text{[EASY]}}\)......