写代码最开始时,为了方便大多写linq method chain,随着业务发展,需要把linq method chain转换成LINQ-expression更方便。
resharper有下面3种,可以重构项目:
- convert linq to method chain
- convert linq to code
- Loop can be converted into LINQ-expression
但没有转换成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