From 8e170e2e7aae8780cc9c0bb2f834c6a08794eefb Mon Sep 17 00:00:00 2001 From: duckdoom5 Date: Tue, 25 Feb 2025 09:51:30 +0100 Subject: [PATCH 1/3] Allow user defined `NewLine` sequence --- src/Generator/Generators/CodeGenerator.cs | 8 +++++--- src/Generator/Passes/GetterSetterToPropertyPass.cs | 4 ++-- src/Generator/Utils/BlockGenerator.cs | 6 +++--- src/Generator/Utils/TextGenerator.cs | 12 ++++++------ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Generator/Generators/CodeGenerator.cs b/src/Generator/Generators/CodeGenerator.cs index e8b8a0b35c..b3d74a31e4 100644 --- a/src/Generator/Generators/CodeGenerator.cs +++ b/src/Generator/Generators/CodeGenerator.cs @@ -140,11 +140,13 @@ public virtual void GenerateComment(RawComment comment) var lines = new List(); - if (comment.BriefText.Contains("\n")) + if (comment.BriefText.Contains('\n')) { + var commentLines = HtmlEncoder.HtmlEncode(comment.BriefText) + .Split('\n', '\r'); + lines.Add(""); - foreach (string line in HtmlEncoder.HtmlEncode(comment.BriefText).Split( - Environment.NewLine.ToCharArray())) + foreach (string line in commentLines) { if (string.IsNullOrWhiteSpace(line)) continue; diff --git a/src/Generator/Passes/GetterSetterToPropertyPass.cs b/src/Generator/Passes/GetterSetterToPropertyPass.cs index a5d0be1a40..0653ab3a0d 100644 --- a/src/Generator/Passes/GetterSetterToPropertyPass.cs +++ b/src/Generator/Passes/GetterSetterToPropertyPass.cs @@ -334,8 +334,8 @@ private static void CombineComments(Property property) Method setter = property.SetMethod; if (getter != setter && setter?.Comment != null) { - comment.BriefText += Environment.NewLine + setter.Comment.BriefText; - comment.Text += Environment.NewLine + setter.Comment.Text; + comment.BriefText += TextGenerator.NewLineChar + setter.Comment.BriefText; + comment.Text += TextGenerator.NewLineChar + setter.Comment.Text; comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks); } } diff --git a/src/Generator/Utils/BlockGenerator.cs b/src/Generator/Utils/BlockGenerator.cs index 1dff992787..56a2b1934b 100644 --- a/src/Generator/Utils/BlockGenerator.cs +++ b/src/Generator/Utils/BlockGenerator.cs @@ -135,12 +135,12 @@ public virtual StringBuilder Generate() if (previousBlock != null && (previousBlock.NewLineKind == NewLineKind.BeforeNextBlock || (previousBlock.NewLineKind == NewLineKind.IfNotEmpty && !previousBlockEmpty))) - builder.AppendLine(); + builder.Append(TextGenerator.NewLineChar); builder.Append(childText); if (childBlock.NewLineKind == NewLineKind.Always) - builder.AppendLine(); + builder.Append(TextGenerator.NewLineChar); previousBlock = childBlock; previousBlockEmpty = childText.Length == 0; @@ -318,7 +318,7 @@ internal ref struct PushedBlock private readonly BlockGenerator generator; private readonly NewLineKind next; - public PushedBlock(BlockGenerator generator, NewLineKind next) + public PushedBlock(BlockGenerator generator, NewLineKind next) { this.generator = generator; this.next = next; diff --git a/src/Generator/Utils/TextGenerator.cs b/src/Generator/Utils/TextGenerator.cs index a57ae0c652..5b52ac0831 100644 --- a/src/Generator/Utils/TextGenerator.cs +++ b/src/Generator/Utils/TextGenerator.cs @@ -21,9 +21,11 @@ public interface ITextGenerator public class TextGenerator : ITextGenerator { + public static string NewLineChar = "\n"; + public const uint DefaultIndentation = 4; - public StringBuilder StringBuilder = new StringBuilder(); + public StringBuilder StringBuilder = new(); public bool IsStartOfLine { get; set; } public bool NeedsNewLine { get; set; } public uint CurrentIndentation { get; set; } @@ -54,11 +56,9 @@ public void Write(string msg, params object[] args) msg = string.Format(msg, args); if (IsStartOfLine && !string.IsNullOrWhiteSpace(msg)) - StringBuilder.Append(new string(' ', - (int)(CurrentIndentation * DefaultIndentation))); + StringBuilder.Append(new string(' ', (int)(CurrentIndentation * DefaultIndentation))); - if (msg.Length > 0) - IsStartOfLine = msg.EndsWith(Environment.NewLine); + IsStartOfLine = msg.Length > 0 && msg.EndsWith(NewLineChar); StringBuilder.Append(msg); } @@ -90,7 +90,7 @@ internal TextBlock WriteBlock((BlockKind kind, string msg) info) public void NewLine() { - StringBuilder.AppendLine(string.Empty); + StringBuilder.Append(NewLineChar); IsStartOfLine = true; } From ad8a3306d25574b8c919c0d559b047a56f7ae861 Mon Sep 17 00:00:00 2001 From: duckdoom5 Date: Tue, 25 Feb 2025 15:13:34 +0100 Subject: [PATCH 2/3] Auto detect line endings --- src/Generator/Driver.cs | 12 +++++ src/Generator/Options.cs | 11 ++++ src/Generator/Utils/ProcessHelper.cs | 76 +++++++++++++++------------- src/Generator/Utils/TextGenerator.cs | 15 +++++- src/Generator/Utils/Utils.cs | 49 ++++++++++++++++++ 5 files changed, 126 insertions(+), 37 deletions(-) diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 856398e948..b8e15ecd3e 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -45,8 +45,20 @@ void ValidateOptions() } if (Options.NoGenIncludeDirs != null) + { foreach (var incDir in Options.NoGenIncludeDirs) ParserOptions.AddIncludeDirs(incDir); + } + + NewLineType newLineType = Options.OutputNewLineType; + if (Options.OutputNewLineType == NewLineType.Auto) + { + var sourceNewLineType = GeneratorHelpers.GetNewLineTypeFromGitConfig(Options.OutputDir); + + newLineType = sourceNewLineType ?? NewLineType.Host; + } + + TextGenerator.SetLineEndingType(newLineType); } public void Setup() diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index fe90828fbe..0d9a00084c 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -15,6 +15,15 @@ public enum GenerationOutputMode FilePerUnit } + public enum NewLineType + { + Auto, // Attempt to auto-detect the new line type using git repository settings of the output directory + Host, // Same as Environment.NewLine on the source generator host + LF, + CR, + CRLF, + } + public class DriverOptions { public DriverOptions() @@ -84,6 +93,8 @@ public bool DoAllModulesHaveLibraries() => public string OutputDir; + public NewLineType OutputNewLineType = NewLineType.Auto; + public bool OutputInteropIncludes; public bool GenerateFunctionTemplates; /// diff --git a/src/Generator/Utils/ProcessHelper.cs b/src/Generator/Utils/ProcessHelper.cs index d482b89ff8..96a562bf20 100644 --- a/src/Generator/Utils/ProcessHelper.cs +++ b/src/Generator/Utils/ProcessHelper.cs @@ -7,46 +7,50 @@ public static class ProcessHelper { public static string Run(string path, string args, out int error, out string errorMessage) { - using (var process = new Process()) + return RunFrom(null, path, args, out error, out errorMessage); + } + + public static string RunFrom(string workingDir, string path, string args, out int error, out string errorMessage) + { + using var process = new Process(); + process.StartInfo.WorkingDirectory = workingDir; + process.StartInfo.FileName = path; + process.StartInfo.Arguments = args; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + + var reterror = new StringBuilder(); + var retout = new StringBuilder(); + process.OutputDataReceived += (sender, outargs) => + { + if (string.IsNullOrEmpty(outargs.Data)) + return; + + if (retout.Length > 0) + retout.AppendLine(); + retout.Append(outargs.Data); + }; + process.ErrorDataReceived += (sender, errargs) => { - process.StartInfo.FileName = path; - process.StartInfo.Arguments = args; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; + if (string.IsNullOrEmpty(errargs.Data)) + return; - var reterror = new StringBuilder(); - var retout = new StringBuilder(); - process.OutputDataReceived += (sender, outargs) => - { - if (!string.IsNullOrEmpty(outargs.Data)) - { - if (retout.Length > 0) - retout.AppendLine(); - retout.Append(outargs.Data); - } - }; - process.ErrorDataReceived += (sender, errargs) => - { - if (!string.IsNullOrEmpty(errargs.Data)) - { - if (reterror.Length > 0) - reterror.AppendLine(); - reterror.Append(errargs.Data); - } - }; + if (reterror.Length > 0) + reterror.AppendLine(); + reterror.Append(errargs.Data); + }; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - process.WaitForExit(); - process.CancelOutputRead(); - process.CancelErrorRead(); + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + process.CancelOutputRead(); + process.CancelErrorRead(); - error = process.ExitCode; - errorMessage = reterror.ToString(); - return retout.ToString(); - } + error = process.ExitCode; + errorMessage = reterror.ToString(); + return retout.ToString(); } } } diff --git a/src/Generator/Utils/TextGenerator.cs b/src/Generator/Utils/TextGenerator.cs index 5b52ac0831..9dbbedffe2 100644 --- a/src/Generator/Utils/TextGenerator.cs +++ b/src/Generator/Utils/TextGenerator.cs @@ -21,7 +21,7 @@ public interface ITextGenerator public class TextGenerator : ITextGenerator { - public static string NewLineChar = "\n"; + public static string NewLineChar; public const uint DefaultIndentation = 4; @@ -47,6 +47,19 @@ public TextGenerator Clone() return new TextGenerator(this); } + public static void SetLineEndingType(NewLineType newLineType) + { + NewLineChar = newLineType switch + { + NewLineType.Host => Environment.NewLine, + NewLineType.CR => "\r", + NewLineType.LF => "\n", + NewLineType.CRLF => "\r\n", + NewLineType.Auto => throw new ArgumentException("Don't use Auto here.", nameof(newLineType)), + _ => throw new ArgumentOutOfRangeException(nameof(newLineType)) + }; + } + public void Write(string msg, params object[] args) { if (string.IsNullOrEmpty(msg)) diff --git a/src/Generator/Utils/Utils.cs b/src/Generator/Utils/Utils.cs index 3a6a1c9b0f..48528579ea 100644 --- a/src/Generator/Utils/Utils.cs +++ b/src/Generator/Utils/Utils.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using CppSharp.Utils; namespace CppSharp { @@ -130,4 +131,52 @@ public static string GetRelativePath(string fromPath, string toPath) return uri1.MakeRelativeUri(uri2).ToString(); } } + + public static class GeneratorHelpers + { + public static NewLineType? GetNewLineTypeFromGitConfig(string outputDir) + { + if (!bool.TryParse( + ProcessHelper.RunFrom(outputDir, + "git", "rev-parse --is-inside-work-tree", + out _, out _ + ), + out bool isInsideWorkTree)) + { + return null; + } + + if (!isInsideWorkTree) + return null; + + // Check git config core.eol setting + var eolConfig = ProcessHelper.RunFrom(outputDir, + "git", "config --get core.eol", + out _, out _) + .Trim().ToLowerInvariant(); + + switch (eolConfig) + { + case "lf": + return NewLineType.LF; + case "crlf": + return NewLineType.CRLF; + } + + // Otherwise check git config core.autocrlf setting + var autoCrLf = ProcessHelper.RunFrom(outputDir, + "git", "config --get core.autocrlf", + out _, out _) + .Trim().ToLowerInvariant(); + + return autoCrLf switch + { + "input" => NewLineType.LF, + "true" => NewLineType.CRLF, + "false" => NewLineType.Host, + + _ => null + }; + } + } } From 307f720ef5ca66d8af9e7e9d05a8d34a08698cfc Mon Sep 17 00:00:00 2001 From: duckdoom5 Date: Tue, 25 Feb 2025 16:15:41 +0100 Subject: [PATCH 3/3] Handle exception if `git` is not found --- src/Generator/Utils/Utils.cs | 77 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/src/Generator/Utils/Utils.cs b/src/Generator/Utils/Utils.cs index 48528579ea..44e777cf9a 100644 --- a/src/Generator/Utils/Utils.cs +++ b/src/Generator/Utils/Utils.cs @@ -84,7 +84,7 @@ public static void TrimUnderscores(this StringBuilder stringBuilder) { while (stringBuilder.Length > 0 && stringBuilder[0] == '_') stringBuilder.Remove(0, 1); - while (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] == '_') + while (stringBuilder.Length > 0 && stringBuilder[^1] == '_') stringBuilder.Remove(stringBuilder.Length - 1, 1); } } @@ -136,47 +136,54 @@ public static class GeneratorHelpers { public static NewLineType? GetNewLineTypeFromGitConfig(string outputDir) { - if (!bool.TryParse( - ProcessHelper.RunFrom(outputDir, - "git", "rev-parse --is-inside-work-tree", - out _, out _ - ), - out bool isInsideWorkTree)) + try { - return null; - } + if (!bool.TryParse( + ProcessHelper.RunFrom(outputDir, + "git", "rev-parse --is-inside-work-tree", + out _, out _ + ), + out bool isInsideWorkTree)) + { + return null; + } - if (!isInsideWorkTree) - return null; + if (!isInsideWorkTree) + return null; - // Check git config core.eol setting - var eolConfig = ProcessHelper.RunFrom(outputDir, - "git", "config --get core.eol", - out _, out _) - .Trim().ToLowerInvariant(); + // Check git config core.eol setting + var eolConfig = ProcessHelper.RunFrom(outputDir, + "git", "config --get core.eol", + out _, out _) + .Trim().ToLowerInvariant(); - switch (eolConfig) - { - case "lf": - return NewLineType.LF; - case "crlf": - return NewLineType.CRLF; - } + switch (eolConfig) + { + case "lf": + return NewLineType.LF; + case "crlf": + return NewLineType.CRLF; + } - // Otherwise check git config core.autocrlf setting - var autoCrLf = ProcessHelper.RunFrom(outputDir, - "git", "config --get core.autocrlf", - out _, out _) - .Trim().ToLowerInvariant(); + // Otherwise check git config core.autocrlf setting + var autoCrLf = ProcessHelper.RunFrom(outputDir, + "git", "config --get core.autocrlf", + out _, out _) + .Trim().ToLowerInvariant(); - return autoCrLf switch - { - "input" => NewLineType.LF, - "true" => NewLineType.CRLF, - "false" => NewLineType.Host, + return autoCrLf switch + { + "input" => NewLineType.LF, + "true" => NewLineType.CRLF, + "false" => NewLineType.Host, - _ => null - }; + _ => null + }; + } + catch (Exception) + { + return null; + } } } }