Skip to content

Commit

Permalink
fix: Handle file-scoped namespaces (#111)
Browse files Browse the repository at this point in the history
and some general namespace suffix's and formatting
  • Loading branch information
BenjaminMichaelis committed Nov 30, 2023
1 parent 95a36fa commit ced82dc
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 43 deletions.
12 changes: 12 additions & 0 deletions ListingManager.Tests/ListingInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,16 @@ public void GetNewFileName_FileName_ReturnCorrectNewFileName(string expected, st

Assert.Equal(expected, new ListingInformation(writtenFile.FullName, isTest).GetNewFileName());
}

[Theory]
[InlineData("namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06;", 0, true, false)]
[InlineData("namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06 ;", 1, true, false)]
[InlineData("namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06{ ", 0, false, true)]
[InlineData("namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06 { ", 1, false, true)]
[InlineData("namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06 { ", 2, false, true)]
public void GetNumberOfWhitespaceCharactersInTheSuffixOfNamespace_Namespace_ReturnCorrectNumberOfWhitespaceCharacters(string @namespace, int expected, bool isNamespaceFileScoped, bool isNamespaceCurlyBraced)
{
Assert.Equal(expected, ListingInformation.GetNumberOfWhitespaceCharactersInTheSuffixOfNamespace(@namespace, isNamespaceFileScoped, isNamespaceCurlyBraced));
}

}
143 changes: 143 additions & 0 deletions ListingManager.Tests/ListingManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,149 @@ public void UpdateChapterListingNumbers_GitStorageManager_ListingsWithinListMiss
}
#endregion GitStorageManager
#region UsingOSStorageManager
[Fact]
public void UpdateChapterListingNumbers_OSStorageManager_FileScopedNamespacesUpdated()
{
ICollection<string> filesToMake = new List<string>
{
Path.Join("Chapter42","Listing42.06.cs"),
};

ICollection<string> expectedFiles = new List<string>
{
Path.Join("Chapter42","Listing42.01.cs")
};

IEnumerable<string> toWrite = new List<string>
{
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06;",
" using System;",
" using System.Reflection;",
" public class Program { }",
};

IEnumerable<string> expectedToWrite = new List<string>
{
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter42.Listing42_01;",
" using System;",
" using System.Reflection;",
" public class Program { }",
};
DirectoryInfo tempDir = CreateTempDirectory();
DirectoryInfo chapterDir = CreateTempDirectory(tempDir, name: "Chapter42");
CreateTempDirectory(tempDir, name: "Chapter42.Tests");
WriteFiles(tempDir, filesToMake, toWrite);
expectedFiles = ConvertFileNamesToFullPath(expectedFiles, tempDir).ToList();

ListingManager listingManager = new(tempDir, new OSStorageManager());
listingManager.UpdateChapterListingNumbers(chapterDir);

List<string> files = FileManager.GetAllFilesAtPath(tempDir.FullName, true)
.Where(x => Path.GetExtension(x) == ".cs").OrderBy(x => x).ToList();

// Assert
string expectedFile = Assert.Single(files);
Assert.Equivalent(expectedFiles, files);

Assert.Equal(string.Join(Environment.NewLine, expectedToWrite) + Environment.NewLine, File.ReadAllText(expectedFile));
}

[Fact]
public void UpdateChapterListingNumbers_OSStorageManager_FileScopedNamespaceWithSpacesUpdated()
{
ICollection<string> filesToMake = new List<string>
{
Path.Join("Chapter42","Listing42.06.cs"),
};

ICollection<string> expectedFiles = new List<string>
{
Path.Join("Chapter42","Listing42.01.cs")
};

IEnumerable<string> toWrite = new List<string>
{
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06 ;",
" using System;",
" using System.Reflection;",
" public class Program { }",
};

IEnumerable<string> expectedToWrite = new List<string>
{
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter42.Listing42_01 ;",
" using System;",
" using System.Reflection;",
" public class Program { }",
};
DirectoryInfo tempDir = CreateTempDirectory();
DirectoryInfo chapterDir = CreateTempDirectory(tempDir, name: "Chapter42");
CreateTempDirectory(tempDir, name: "Chapter42.Tests");
WriteFiles(tempDir, filesToMake, toWrite);
expectedFiles = ConvertFileNamesToFullPath(expectedFiles, tempDir).ToList();

ListingManager listingManager = new(tempDir, new OSStorageManager());
listingManager.UpdateChapterListingNumbers(chapterDir);

List<string> files = FileManager.GetAllFilesAtPath(tempDir.FullName, true)
.Where(x => Path.GetExtension(x) == ".cs").OrderBy(x => x).ToList();

// Assert
string expectedFile = Assert.Single(files);
Assert.Equivalent(expectedFiles, files);

Assert.Equal(string.Join(Environment.NewLine, expectedToWrite) + Environment.NewLine, File.ReadAllText(expectedFile));
}

[Fact]
public void UpdateChapterListingNumbers_OSStorageManager_NamespacesWithCurlyBraceOnSameLineUpdated()
{
ICollection<string> filesToMake = new List<string>
{
Path.Join("Chapter42","Listing42.06.cs"),
};

ICollection<string> expectedFiles = new List<string>
{
Path.Join("Chapter42","Listing42.01.cs")
};

IEnumerable<string> toWrite = new List<string>
{
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter18.Listing18_06 {",
" using System;",
" using System.Reflection;",
" public class Program { }",
"}"
};

IEnumerable<string> expectedToWrite = new List<string>
{
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter42.Listing42_01 {",
" using System;",
" using System.Reflection;",
" public class Program { }",
"}",
};
DirectoryInfo tempDir = CreateTempDirectory();
DirectoryInfo chapterDir = CreateTempDirectory(tempDir, name: "Chapter42");
CreateTempDirectory(tempDir, name: "Chapter42.Tests");
WriteFiles(tempDir, filesToMake, toWrite);
expectedFiles = ConvertFileNamesToFullPath(expectedFiles, tempDir).ToList();

ListingManager listingManager = new(tempDir, new OSStorageManager());
listingManager.UpdateChapterListingNumbers(chapterDir);

List<string> files = FileManager.GetAllFilesAtPath(tempDir.FullName, true)
.Where(x => Path.GetExtension(x) == ".cs").OrderBy(x => x).ToList();

// Assert
string expectedFile = Assert.Single(files);
Assert.Equivalent(expectedFiles, files);

Assert.Equal(string.Join(Environment.NewLine, expectedToWrite) + Environment.NewLine, File.ReadAllText(expectedFile));
}

[Fact]
public void UpdateChapterListingNumbers_ListingsWithinListMissing_ListingsRenumbered()
{
Expand Down
108 changes: 65 additions & 43 deletions ListingManager/ListingInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,57 +119,79 @@ public string GetNewNamespace()
+ $".Chapter{paddedChapterNumber}"
+ $".Listing{paddedChapterNumber}_"
+ paddedListingNumber + (IsTest ? ".Tests" : string.Empty);
}

}

public bool UpdateNamespaceInFileContents()
{
return UpdateNamespaceInFileContents(GetNewNamespace());
{
return UpdateNamespaceInFileContents(GetNewNamespace());
}

public bool UpdateNamespaceInFileContents(string newNamespace)
{
bool updated = false;
for (int i = 0; i < FileContents.Count; i++)
{
if (FileContents[i].TrimStart().StartsWith("namespace"))
{
FileContents[i] = $"namespace {newNamespace}";
updated = true;
FileContentsChanged = true;
}
}
return updated;
}

{
bool updated = false;
for (int i = 0; i < FileContents.Count; i++)
{
if (FileContents[i].TrimStart().StartsWith("namespace"))
{
bool isNamespaceFileScoped = FileContents[i].TrimEnd().EndsWith(';');
bool isNamespaceCurlyBraced = FileContents[i].TrimEnd().EndsWith('{');
if (isNamespaceFileScoped && isNamespaceCurlyBraced) throw new InvalidOperationException("Namespace is detected to be both file scoped and curly braced scoped.");
int numberOfWhitespaceCharacters = GetNumberOfWhitespaceCharactersInTheSuffixOfNamespace(FileContents[i], isNamespaceFileScoped, isNamespaceCurlyBraced);
FileContents[i] = $"namespace {newNamespace}";
// add the whitespace characters back in
FileContents[i] += new string(' ', numberOfWhitespaceCharacters);
// append the closing curly brace if the namespace was curly braced and a ; if it was file scoped
if (isNamespaceFileScoped) FileContents[i] += ";";
else if (isNamespaceCurlyBraced) FileContents[i] += "{";
updated = true;
FileContentsChanged = true;
}
}
return updated;
}

public static int GetNumberOfWhitespaceCharactersInTheSuffixOfNamespace(string @namespace, bool isNamespaceFileScoped, bool isNamespaceCurlyBraced)
{
int indexOfNamespaceEnd = @namespace.TrimEnd().LastIndexOfAny([';', '{']);
// get the number of whitespace characters between the last non-whitespace character and the index indexOfNamespaceEnd
int numberOfWhitespaceCharacters = 0;
if (indexOfNamespaceEnd > 0 && (isNamespaceCurlyBraced || isNamespaceFileScoped))
{
numberOfWhitespaceCharacters = indexOfNamespaceEnd - @namespace[..(indexOfNamespaceEnd)].TrimEnd().Length;
}

return numberOfWhitespaceCharacters;
}

public void UpdateReferencesInFileAndTest(List<ListingInformation> listingData)
{
for (int i = 0; i < FileContents.Count; i++)
{
if (ListingReference().IsMatch(FileContents[i]))
{
if (FileContents[i].TrimStart().StartsWith("namespace"))
{
continue;
}
MatchCollection matches = ListingReference().Matches(FileContents[i]);
for (int j = 0; j < matches.Count; j++)
{
int chapterNumber = int.Parse(matches[j].Groups[1].Value);
string chapterListingDeliminator = matches[j].Groups[2].Value;
{
if (FileContents[i].TrimStart().StartsWith("namespace"))
{
continue;
}
MatchCollection matches = ListingReference().Matches(FileContents[i]);
for (int j = 0; j < matches.Count; j++)
{
int chapterNumber = int.Parse(matches[j].Groups[1].Value);
string chapterListingDeliminator = matches[j].Groups[2].Value;
int listingNumber = int.Parse(matches[j].Groups[3].Value);
ListingInformation? referencedListingInformation = listingData.FirstOrDefault(item => item.OriginalChapterNumber == chapterNumber && item.OriginalListingNumber == listingNumber);
if (referencedListingInformation is not null)
{
string replacementListingReference = matches[j].Groups[0].Value.Replace($"{chapterNumber:D2}{matches[j].Groups[2].Value}{listingNumber:D2}", $"{referencedListingInformation.NewChapterNumber:D2}{matches[j].Groups[2].Value}{referencedListingInformation.NewListingNumber:D2}");
ListingInformation? referencedListingInformation = listingData.FirstOrDefault(item => item.OriginalChapterNumber == chapterNumber && item.OriginalListingNumber == listingNumber);
if (referencedListingInformation is not null)
{
string replacementListingReference = matches[j].Groups[0].Value.Replace($"{chapterNumber:D2}{matches[j].Groups[2].Value}{listingNumber:D2}", $"{referencedListingInformation.NewChapterNumber:D2}{matches[j].Groups[2].Value}{referencedListingInformation.NewListingNumber:D2}");
FileContents[i] = FileContents[i].Replace(matches[j].Groups[0].Value, replacementListingReference);
}
}
}
}
FileContentsChanged = true;
}
}
if (AssociatedTest is ListingInformation listingTest)
{
listingTest.UpdateReferencesInFileAndTest(listingData);
if (AssociatedTest is ListingInformation listingTest)
{
listingTest.UpdateReferencesInFileAndTest(listingData);
}
}

Expand All @@ -183,13 +205,13 @@ public string GetNewFileName()
paddedChapterNumber,
paddedListingNumber,
string.IsNullOrWhiteSpace(Caption) ? "" : $".{Caption}");
}

// Match any approved files regex: regexr.com/7lfi2
}

// Match any approved files regex: regexr.com/7lfi2
[GeneratedRegex("Listing(\\d{2}).(\\d{2})([A-Za-z]*)(\\.{1}(.*))*(\\.(\\w+))$")]
private static partial Regex ExtractListingNameFromAnyApprovedFileTypes();
[GeneratedRegex(@"\d{1}[A-Za-z]")]
private static partial Regex SingleDigitListingWithSuffix();
[GeneratedRegex("Listing(\\d{2})([_.])(\\d{2})")]
private static partial Regex ListingReference();
private static partial Regex SingleDigitListingWithSuffix();
[GeneratedRegex("Listing(\\d{2})([_.])(\\d{2})")]
private static partial Regex ListingReference();
}

0 comments on commit ced82dc

Please sign in to comment.