朋友遇到的一個問題, 他有個大概兩千行的類別, 裡面有一坨欄位, 因為之前的工程師偷懶所以都用小寫當類別屬性 LOL ~ 他希望把他轉為大寫駝峰, 就 vibe coding 搞看看 記得以前自己也弄過類似的功能, 不過是用 visual studio 燈泡的那種功能手動一個一個屬性去加 需要安裝 SymSpell Microsoft.CodeAnalysis.CSharp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 using System;using System.IO;using System.Linq;using System.Text;using System.Globalization;using Microsoft.CodeAnalysis;using Microsoft.CodeAnalysis.CSharp;using Microsoft.CodeAnalysis.CSharp.Syntax;namespace CodeRefactorTool { class Program { static void Main (string [] args ) { string dictPath = "frequency_dictionary_en_82_765.txt" ; var symSpell = new SymSpell(82765 , 0 ); if (!symSpell.LoadDictionary(dictPath, 0 , 1 )) { Console.WriteLine("錯誤:找不到詞典檔!" ); return ; } string filePath = @"shit_code.cs" ; if (!File.Exists(filePath)) { Console.WriteLine($"檔案不存在:{filePath} " ); return ; } ProcessFile(filePath, symSpell); Console.WriteLine("\n重構完成!註解已保留在最上方。" ); Console.ReadKey(); } static void ProcessFile (string filePath, SymSpell symSpell ) { string sourceCode = File.ReadAllText(filePath); SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode); var root = (CompilationUnitSyntax)tree.GetCompilationUnitRoot(); if (!root.Usings.Any(u => u.Name.ToString() == "System.Text.Json.Serialization" )) { root = root.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Text.Json.Serialization" ))); } var rewriter = new JsonAttributeTopRewriter(symSpell); var result = rewriter.Visit(root); File.WriteAllText(filePath, result.ToFullString(), Encoding.UTF8); } } class JsonAttributeTopRewriter : CSharpSyntaxRewriter { private readonly SymSpell _symSpell; private readonly TextInfo _textInfo = CultureInfo.InvariantCulture.TextInfo; public JsonAttributeTopRewriter (SymSpell symSpell ) => _symSpell = symSpell; public override SyntaxNode VisitPropertyDeclaration (PropertyDeclarationSyntax node ) { string propertyName = node.Identifier.Text; var symResult = _symSpell.WordSegmentation(propertyName); string pascalName = string .Concat(symResult.segmentedString.Split(' ' ).Select(w => _textInfo.ToTitleCase(w))); if (node.AttributeLists.Any(al => al.Attributes.Any(a => a.Name.ToString().Contains("JsonPropertyName" )))) return node; var allLeadingTrivia = node.GetLeadingTrivia(); var lastWhitespace = allLeadingTrivia.LastOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia)); var attribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("JsonPropertyName" )) .AddArgumentListArguments(SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression($"\"{pascalName} \"" ))); var newAttrList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(attribute)) .WithLeadingTrivia(allLeadingTrivia) .WithTrailingTrivia(SyntaxFactory.EndOfLine("\r\n" )); var updatedAttributeLists = node.AttributeLists; if (updatedAttributeLists.Count > 0 ) { var firstList = updatedAttributeLists[0 ]; updatedAttributeLists = updatedAttributeLists.Replace(firstList, firstList.WithLeadingTrivia(SyntaxFactory.TriviaList(lastWhitespace))); } var newNode = node.WithLeadingTrivia(SyntaxFactory.TriviaList(lastWhitespace)) .WithAttributeLists(updatedAttributeLists.Insert(0 , newAttrList)); return newNode; } } }