Skip to content

Commit 7e71133

Browse files
committed
Trim indent/newlines in text-based package metadata
This allows having newlines in the csproj while still compacting for packing. The trimming involves: * Removing blank lines at the beginning and ending of the value * Determining indenting on the first line and using that to remove subsequent indenting in all following lines. * Applying markdown rules to remove newlines: meaning only full paragraph separating newlines (blank) will be kept. * Collapse multiple blank newlines into just one (when separating paragraphs). This means you can indent nicely in the XML (as VS does by default) while still keeping a clean and compact representation in the package. For example: ```xml <Description> Create, edit and run multiple C# top-level programs in the same project, respecting per-file `#:package` references and `#:property` 😍 </Description> ``` Would become a single line of text.
1 parent 5a3b5fe commit 7e71133

File tree

3 files changed

+108
-7
lines changed

3 files changed

+108
-7
lines changed

src/NuGetizer.Tasks/CreatePackage.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ public Manifest CreateManifest()
9292
metadata.DevelopmentDependency = true;
9393

9494
if (Manifest.TryGetMetadata("Title", out var title))
95-
metadata.Title = title;
95+
metadata.Title = title.TrimIndent();
9696

9797
if (Manifest.TryGetMetadata("Description", out var description))
98-
metadata.Description = description;
98+
metadata.Description = description.TrimIndent();
9999

100100
if (Manifest.TryGetMetadata("Summary", out var summary))
101-
metadata.Summary = summary;
101+
metadata.Summary = summary.TrimIndent();
102102

103103
if (Manifest.TryGetMetadata("Readme", out var readme))
104104
metadata.Readme = readme;
@@ -171,7 +171,7 @@ public Manifest CreateManifest()
171171
metadata.Icon = icon;
172172

173173
if (Manifest.TryGetMetadata("ReleaseNotes", out var releaseNotes))
174-
metadata.ReleaseNotes = releaseNotes;
174+
metadata.ReleaseNotes = releaseNotes.TrimIndent();
175175

176176
if (Manifest.TryGetMetadata("Tags", out var tags))
177177
metadata.Tags = tags;

src/NuGetizer.Tasks/Extensions.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,91 @@ public static Manifest GetManifest(this IPackageCoreReader packageReader)
131131
return null;
132132
}
133133

134+
public static string ReplaceLineEndings(this string value)
135+
{
136+
if (string.IsNullOrEmpty(value))
137+
return value;
138+
139+
return value.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine);
140+
}
141+
142+
public static string TrimIndent(this string value)
143+
{
144+
if (string.IsNullOrWhiteSpace(value))
145+
return "";
146+
147+
// Read lines without the newline characters
148+
using var sr = new StringReader(value);
149+
var lines = new List<string>();
150+
string? line;
151+
while ((line = sr.ReadLine()) != null)
152+
{
153+
lines.Add(line);
154+
}
155+
156+
// Find the first non-empty line
157+
var start = 0;
158+
while (start < lines.Count && string.IsNullOrWhiteSpace(lines[start]))
159+
{
160+
start++;
161+
}
162+
if (start == lines.Count)
163+
return "";
164+
165+
// Determine the indentation prefix from the first content line
166+
var firstContent = lines[start];
167+
var indentLen = 0;
168+
while (indentLen < firstContent.Length && char.IsWhiteSpace(firstContent[indentLen]))
169+
{
170+
indentLen++;
171+
}
172+
var indentPrefix = firstContent.Substring(0, indentLen);
173+
174+
// Find the last non-empty line
175+
var end = lines.Count - 1;
176+
while (end >= start && string.IsNullOrWhiteSpace(lines[end]))
177+
{
178+
end--;
179+
}
180+
181+
// Trim indentation from each line
182+
var trimmedLines = new List<string>();
183+
for (var i = start; i <= end; i++)
184+
{
185+
var ln = lines[i];
186+
var trimmed = ln.StartsWith(indentPrefix) ? ln.Substring(indentPrefix.Length) : ln;
187+
trimmedLines.Add(trimmed);
188+
}
189+
190+
// Collapse like Markdown: join lines within paragraphs with space, paragraphs separated by double newline
191+
var paragraphs = new List<string>();
192+
var currentPara = new List<string>();
193+
for (var i = 0; i < trimmedLines.Count; i++)
194+
{
195+
var ln = trimmedLines[i];
196+
if (string.IsNullOrWhiteSpace(ln))
197+
{
198+
if (currentPara.Count > 0)
199+
{
200+
paragraphs.Add(string.Join(" ", currentPara.Select(l => l.TrimEnd())));
201+
currentPara.Clear();
202+
}
203+
// Skip blanks, multiple blanks collapse to one break
204+
}
205+
else
206+
{
207+
currentPara.Add(ln);
208+
}
209+
}
210+
if (currentPara.Count > 0)
211+
{
212+
paragraphs.Add(string.Join(" ", currentPara.Select(l => l.TrimEnd())));
213+
}
214+
215+
// Join paragraphs with double newline
216+
return string.Join(Environment.NewLine + Environment.NewLine, paragraphs);
217+
}
218+
134219
public static void LogErrorCode(this TaskLoggingHelper log, string code, string message, params object[] messageArgs) =>
135220
log.LogError(string.Empty, code, string.Empty, string.Empty, 0, 0, 0, 0, message, messageArgs);
136221

src/NuGetizer.Tests/CreatePackageTests.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Diagnostics;
43
using System.IO;
54
using System.IO.Compression;
@@ -35,7 +34,16 @@ public CreatePackageTests(ITestOutputHelper output)
3534
{ MetadataName.PackageId, "package" },
3635
{ MetadataName.Version, "1.0.0" },
3736
{ "Title", "title" },
38-
{ "Description", "description" },
37+
{ "Description",
38+
"""
39+
40+
This is the description.
41+
Indent will be trimmed.
42+
43+
44+
New paragraph preserved.
45+
46+
""" },
3947
{ "Summary", "summary" },
4048
{ "Language", "en" },
4149

@@ -111,7 +119,6 @@ public void when_creating_package_then_contains_all_metadata()
111119
Assert.Equal(task.Manifest.GetMetadata("PackageId"), metadata.Id);
112120
Assert.Equal(task.Manifest.GetMetadata("Version"), metadata.Version.ToString());
113121
Assert.Equal(task.Manifest.GetMetadata("Title"), metadata.Title);
114-
Assert.Equal(task.Manifest.GetMetadata("Description"), metadata.Description);
115122
Assert.Equal(task.Manifest.GetMetadata("Summary"), metadata.Summary);
116123
Assert.Equal(task.Manifest.GetMetadata("Language"), metadata.Language);
117124
Assert.Equal(task.Manifest.GetMetadata("Copyright"), metadata.Copyright);
@@ -126,6 +133,15 @@ public void when_creating_package_then_contains_all_metadata()
126133
Assert.Equal(PackageType.Dependency.Name, item.Name);
127134
Assert.Equal(PackageType.EmptyVersion, item.Version);
128135
});
136+
137+
// C#-style triming is applied.
138+
Assert.Equal(
139+
"""
140+
This is the description. Indent will be trimmed.
141+
142+
New paragraph preserved.
143+
""".ReplaceLineEndings(),
144+
metadata.Description.ReplaceLineEndings());
129145
}
130146

131147
[Fact]

0 commit comments

Comments
 (0)