Skip to content

Commit 0f3badd

Browse files
authored
Allow user to set generator newline output type (#1915)
* Allow user defined `NewLine` sequence * Auto detect line endings * Handle exception if `git` is not found
1 parent ba751a5 commit 0f3badd

File tree

8 files changed

+149
-51
lines changed

8 files changed

+149
-51
lines changed

src/Generator/Driver.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,20 @@ void ValidateOptions()
4545
}
4646

4747
if (Options.NoGenIncludeDirs != null)
48+
{
4849
foreach (var incDir in Options.NoGenIncludeDirs)
4950
ParserOptions.AddIncludeDirs(incDir);
51+
}
52+
53+
NewLineType newLineType = Options.OutputNewLineType;
54+
if (Options.OutputNewLineType == NewLineType.Auto)
55+
{
56+
var sourceNewLineType = GeneratorHelpers.GetNewLineTypeFromGitConfig(Options.OutputDir);
57+
58+
newLineType = sourceNewLineType ?? NewLineType.Host;
59+
}
60+
61+
TextGenerator.SetLineEndingType(newLineType);
5062
}
5163

5264
public void Setup()

src/Generator/Generators/CodeGenerator.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ public virtual void GenerateComment(RawComment comment)
140140

141141
var lines = new List<string>();
142142

143-
if (comment.BriefText.Contains("\n"))
143+
if (comment.BriefText.Contains('\n'))
144144
{
145+
var commentLines = HtmlEncoder.HtmlEncode(comment.BriefText)
146+
.Split('\n', '\r');
147+
145148
lines.Add("<summary>");
146-
foreach (string line in HtmlEncoder.HtmlEncode(comment.BriefText).Split(
147-
Environment.NewLine.ToCharArray()))
149+
foreach (string line in commentLines)
148150
{
149151
if (string.IsNullOrWhiteSpace(line))
150152
continue;

src/Generator/Options.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public enum GenerationOutputMode
1515
FilePerUnit
1616
}
1717

18+
public enum NewLineType
19+
{
20+
Auto, // Attempt to auto-detect the new line type using git repository settings of the output directory
21+
Host, // Same as Environment.NewLine on the source generator host
22+
LF,
23+
CR,
24+
CRLF,
25+
}
26+
1827
public class DriverOptions
1928
{
2029
public DriverOptions()
@@ -84,6 +93,8 @@ public bool DoAllModulesHaveLibraries() =>
8493

8594
public string OutputDir;
8695

96+
public NewLineType OutputNewLineType = NewLineType.Auto;
97+
8798
public bool OutputInteropIncludes;
8899
public bool GenerateFunctionTemplates;
89100
/// <summary>

src/Generator/Passes/GetterSetterToPropertyPass.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,8 @@ private static void CombineComments(Property property)
334334
Method setter = property.SetMethod;
335335
if (getter != setter && setter?.Comment != null)
336336
{
337-
comment.BriefText += Environment.NewLine + setter.Comment.BriefText;
338-
comment.Text += Environment.NewLine + setter.Comment.Text;
337+
comment.BriefText += TextGenerator.NewLineChar + setter.Comment.BriefText;
338+
comment.Text += TextGenerator.NewLineChar + setter.Comment.Text;
339339
comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks);
340340
}
341341
}

src/Generator/Utils/BlockGenerator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,12 @@ public virtual StringBuilder Generate()
135135
if (previousBlock != null &&
136136
(previousBlock.NewLineKind == NewLineKind.BeforeNextBlock ||
137137
(previousBlock.NewLineKind == NewLineKind.IfNotEmpty && !previousBlockEmpty)))
138-
builder.AppendLine();
138+
builder.Append(TextGenerator.NewLineChar);
139139

140140
builder.Append(childText);
141141

142142
if (childBlock.NewLineKind == NewLineKind.Always)
143-
builder.AppendLine();
143+
builder.Append(TextGenerator.NewLineChar);
144144

145145
previousBlock = childBlock;
146146
previousBlockEmpty = childText.Length == 0;
@@ -318,7 +318,7 @@ internal ref struct PushedBlock
318318
private readonly BlockGenerator generator;
319319
private readonly NewLineKind next;
320320

321-
public PushedBlock(BlockGenerator generator, NewLineKind next)
321+
public PushedBlock(BlockGenerator generator, NewLineKind next)
322322
{
323323
this.generator = generator;
324324
this.next = next;

src/Generator/Utils/ProcessHelper.cs

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,50 @@ public static class ProcessHelper
77
{
88
public static string Run(string path, string args, out int error, out string errorMessage)
99
{
10-
using (var process = new Process())
10+
return RunFrom(null, path, args, out error, out errorMessage);
11+
}
12+
13+
public static string RunFrom(string workingDir, string path, string args, out int error, out string errorMessage)
14+
{
15+
using var process = new Process();
16+
process.StartInfo.WorkingDirectory = workingDir;
17+
process.StartInfo.FileName = path;
18+
process.StartInfo.Arguments = args;
19+
process.StartInfo.UseShellExecute = false;
20+
process.StartInfo.RedirectStandardOutput = true;
21+
process.StartInfo.RedirectStandardError = true;
22+
23+
var reterror = new StringBuilder();
24+
var retout = new StringBuilder();
25+
process.OutputDataReceived += (sender, outargs) =>
26+
{
27+
if (string.IsNullOrEmpty(outargs.Data))
28+
return;
29+
30+
if (retout.Length > 0)
31+
retout.AppendLine();
32+
retout.Append(outargs.Data);
33+
};
34+
process.ErrorDataReceived += (sender, errargs) =>
1135
{
12-
process.StartInfo.FileName = path;
13-
process.StartInfo.Arguments = args;
14-
process.StartInfo.UseShellExecute = false;
15-
process.StartInfo.RedirectStandardOutput = true;
16-
process.StartInfo.RedirectStandardError = true;
36+
if (string.IsNullOrEmpty(errargs.Data))
37+
return;
1738

18-
var reterror = new StringBuilder();
19-
var retout = new StringBuilder();
20-
process.OutputDataReceived += (sender, outargs) =>
21-
{
22-
if (!string.IsNullOrEmpty(outargs.Data))
23-
{
24-
if (retout.Length > 0)
25-
retout.AppendLine();
26-
retout.Append(outargs.Data);
27-
}
28-
};
29-
process.ErrorDataReceived += (sender, errargs) =>
30-
{
31-
if (!string.IsNullOrEmpty(errargs.Data))
32-
{
33-
if (reterror.Length > 0)
34-
reterror.AppendLine();
35-
reterror.Append(errargs.Data);
36-
}
37-
};
39+
if (reterror.Length > 0)
40+
reterror.AppendLine();
41+
reterror.Append(errargs.Data);
42+
};
3843

39-
process.Start();
40-
process.BeginOutputReadLine();
41-
process.BeginErrorReadLine();
42-
process.WaitForExit();
43-
process.CancelOutputRead();
44-
process.CancelErrorRead();
44+
process.Start();
45+
process.BeginOutputReadLine();
46+
process.BeginErrorReadLine();
47+
process.WaitForExit();
48+
process.CancelOutputRead();
49+
process.CancelErrorRead();
4550

46-
error = process.ExitCode;
47-
errorMessage = reterror.ToString();
48-
return retout.ToString();
49-
}
51+
error = process.ExitCode;
52+
errorMessage = reterror.ToString();
53+
return retout.ToString();
5054
}
5155
}
5256
}

src/Generator/Utils/TextGenerator.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public interface ITextGenerator
2121

2222
public class TextGenerator : ITextGenerator
2323
{
24+
public static string NewLineChar;
25+
2426
public const uint DefaultIndentation = 4;
2527

26-
public StringBuilder StringBuilder = new StringBuilder();
28+
public StringBuilder StringBuilder = new();
2729
public bool IsStartOfLine { get; set; }
2830
public bool NeedsNewLine { get; set; }
2931
public uint CurrentIndentation { get; set; }
@@ -45,6 +47,19 @@ public TextGenerator Clone()
4547
return new TextGenerator(this);
4648
}
4749

50+
public static void SetLineEndingType(NewLineType newLineType)
51+
{
52+
NewLineChar = newLineType switch
53+
{
54+
NewLineType.Host => Environment.NewLine,
55+
NewLineType.CR => "\r",
56+
NewLineType.LF => "\n",
57+
NewLineType.CRLF => "\r\n",
58+
NewLineType.Auto => throw new ArgumentException("Don't use Auto here.", nameof(newLineType)),
59+
_ => throw new ArgumentOutOfRangeException(nameof(newLineType))
60+
};
61+
}
62+
4863
public void Write(string msg, params object[] args)
4964
{
5065
if (string.IsNullOrEmpty(msg))
@@ -54,11 +69,9 @@ public void Write(string msg, params object[] args)
5469
msg = string.Format(msg, args);
5570

5671
if (IsStartOfLine && !string.IsNullOrWhiteSpace(msg))
57-
StringBuilder.Append(new string(' ',
58-
(int)(CurrentIndentation * DefaultIndentation)));
72+
StringBuilder.Append(new string(' ', (int)(CurrentIndentation * DefaultIndentation)));
5973

60-
if (msg.Length > 0)
61-
IsStartOfLine = msg.EndsWith(Environment.NewLine);
74+
IsStartOfLine = msg.Length > 0 && msg.EndsWith(NewLineChar);
6275

6376
StringBuilder.Append(msg);
6477
}
@@ -90,7 +103,7 @@ internal TextBlock WriteBlock((BlockKind kind, string msg) info)
90103

91104
public void NewLine()
92105
{
93-
StringBuilder.AppendLine(string.Empty);
106+
StringBuilder.Append(NewLineChar);
94107
IsStartOfLine = true;
95108
}
96109

src/Generator/Utils/Utils.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Reflection;
55
using System.Text;
66
using System.Text.RegularExpressions;
7+
using CppSharp.Utils;
78

89
namespace CppSharp
910
{
@@ -83,7 +84,7 @@ public static void TrimUnderscores(this StringBuilder stringBuilder)
8384
{
8485
while (stringBuilder.Length > 0 && stringBuilder[0] == '_')
8586
stringBuilder.Remove(0, 1);
86-
while (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] == '_')
87+
while (stringBuilder.Length > 0 && stringBuilder[^1] == '_')
8788
stringBuilder.Remove(stringBuilder.Length - 1, 1);
8889
}
8990
}
@@ -130,4 +131,59 @@ public static string GetRelativePath(string fromPath, string toPath)
130131
return uri1.MakeRelativeUri(uri2).ToString();
131132
}
132133
}
134+
135+
public static class GeneratorHelpers
136+
{
137+
public static NewLineType? GetNewLineTypeFromGitConfig(string outputDir)
138+
{
139+
try
140+
{
141+
if (!bool.TryParse(
142+
ProcessHelper.RunFrom(outputDir,
143+
"git", "rev-parse --is-inside-work-tree",
144+
out _, out _
145+
),
146+
out bool isInsideWorkTree))
147+
{
148+
return null;
149+
}
150+
151+
if (!isInsideWorkTree)
152+
return null;
153+
154+
// Check git config core.eol setting
155+
var eolConfig = ProcessHelper.RunFrom(outputDir,
156+
"git", "config --get core.eol",
157+
out _, out _)
158+
.Trim().ToLowerInvariant();
159+
160+
switch (eolConfig)
161+
{
162+
case "lf":
163+
return NewLineType.LF;
164+
case "crlf":
165+
return NewLineType.CRLF;
166+
}
167+
168+
// Otherwise check git config core.autocrlf setting
169+
var autoCrLf = ProcessHelper.RunFrom(outputDir,
170+
"git", "config --get core.autocrlf",
171+
out _, out _)
172+
.Trim().ToLowerInvariant();
173+
174+
return autoCrLf switch
175+
{
176+
"input" => NewLineType.LF,
177+
"true" => NewLineType.CRLF,
178+
"false" => NewLineType.Host,
179+
180+
_ => null
181+
};
182+
}
183+
catch (Exception)
184+
{
185+
return null;
186+
}
187+
}
188+
}
133189
}

0 commit comments

Comments
 (0)