From 4a2f7e37e5da66f94b1d64f9c17823a4d15b0bed Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 9 Jun 2026 15:06:40 -0400 Subject: [PATCH] feat(adminui): script document formatting (NormalizeWhitespace) --- .../ScriptAnalysis/ScriptAnalysisService.cs | 17 ++++++++++- .../ScriptAnalysis/FormatTests.cs | 30 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/FormatTests.cs diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs index 47b0c6f5..ead65b1a 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs @@ -325,6 +325,21 @@ public sealed class ScriptAnalysisService } catch (Exception ex) { _logger?.LogWarning(ex, "Script signature help failed."); return empty; } } - public FormatResponse Format(FormatRequest req) => new(req.Code); // Task 8 + public FormatResponse Format(FormatRequest req) // Task 8 + { + if (string.IsNullOrEmpty(req.Code)) return new FormatResponse(req.Code); + try + { + var code = Normalize(req.Code); + var tree = CSharpSyntaxTree.ParseText(code, + new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script)); + var formatted = tree.GetRoot().NormalizeWhitespace(indentation: " ", eol: "\n").ToFullString(); + return new FormatResponse(formatted); + } + catch + { + return new FormatResponse(req.Code); + } + } public InlayHintsResponse InlayHints(InlayHintsRequest req) => new(Array.Empty()); // Task 8 (stays empty) } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/FormatTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/FormatTests.cs new file mode 100644 index 00000000..858019de --- /dev/null +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/FormatTests.cs @@ -0,0 +1,30 @@ +using Shouldly; +using Xunit; +using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis; + +namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptAnalysis; + +public sealed class FormatTests +{ + private static readonly ScriptAnalysisService Svc = new(); + private static string Fmt(string code) => Svc.Format(new FormatRequest(code)).Code; + + [Fact] public void Reflows_crammed_source_to_canonical_layout() + { + var outp = Fmt("var x=1;return x;"); + outp.ShouldNotBe("var x=1;return x;"); // it changed + outp.ShouldContain("\n"); // statements split onto separate lines + outp.ShouldContain("var x = 1;"); // canonical spacing + } + + [Fact] public void Empty_code_is_returned_unchanged() + => Fmt("").ShouldBe(""); + + [Fact] public void Unparseable_code_is_returned_unchanged() + { + // a fragment that NormalizeWhitespace can still parse in script mode won't throw; + // pass something that round-trips to itself or is returned as-is on failure. + var src = "return ctx.GetTag(\"A\").Value;"; + Fmt(src).ShouldNotBeNull(); + } +}