Skip to content

Commit 4268611

Browse files
authored
feat(namespace): update using statements across solution (#30)
1 parent 22efe75 commit 4268611

File tree

2 files changed

+124
-2
lines changed

2 files changed

+124
-2
lines changed

src/CodingWithCalvin.ProjectRenamifier/Commands/RenamifyProjectCommand.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,11 @@ private void RenameProject(Project project, DTE2 dte)
105105
// Re-add project to solution with new path
106106
dte.Solution.AddFromFile(projectFilePath);
107107

108+
// Update using statements across the entire solution
109+
SourceFileService.UpdateUsingStatementsInSolution(dte.Solution, currentName, newName);
110+
108111
// TODO: Implement remaining rename operations
109112
// See open issues for requirements:
110-
// - #9: Update using statements across solution
111113
// - #11: Solution folder support
112114
// - #12: Progress indication
113115
// - #13: Error handling and rollback

src/CodingWithCalvin.ProjectRenamifier/Services/SourceFileService.cs

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,134 @@
11
using System.IO;
22
using System.Text;
33
using System.Text.RegularExpressions;
4+
using EnvDTE;
45

56
namespace CodingWithCalvin.ProjectRenamifier.Services
67
{
78
/// <summary>
8-
/// Service for updating namespace declarations in source files.
9+
/// Service for updating namespace declarations and using statements in source files.
910
/// </summary>
1011
internal static class SourceFileService
1112
{
13+
/// <summary>
14+
/// Updates using statements in all .cs files across the entire solution.
15+
/// </summary>
16+
/// <param name="solution">The solution to scan.</param>
17+
/// <param name="oldNamespace">The old namespace to find.</param>
18+
/// <param name="newNamespace">The new namespace to replace with.</param>
19+
/// <returns>The number of files modified.</returns>
20+
public static int UpdateUsingStatementsInSolution(Solution solution, string oldNamespace, string newNamespace)
21+
{
22+
ThreadHelper.ThrowIfNotOnUIThread();
23+
24+
var modifiedCount = 0;
25+
26+
foreach (Project project in solution.Projects)
27+
{
28+
modifiedCount += UpdateUsingStatementsInProjectTree(project, oldNamespace, newNamespace);
29+
}
30+
31+
return modifiedCount;
32+
}
33+
34+
/// <summary>
35+
/// Recursively updates using statements in a project (handles solution folders).
36+
/// </summary>
37+
private static int UpdateUsingStatementsInProjectTree(Project project, string oldNamespace, string newNamespace)
38+
{
39+
ThreadHelper.ThrowIfNotOnUIThread();
40+
41+
if (project == null)
42+
{
43+
return 0;
44+
}
45+
46+
// Handle solution folders
47+
if (project.Kind == EnvDTE.Constants.vsProjectKindSolutionItems)
48+
{
49+
var count = 0;
50+
foreach (ProjectItem item in project.ProjectItems)
51+
{
52+
if (item.SubProject != null)
53+
{
54+
count += UpdateUsingStatementsInProjectTree(item.SubProject, oldNamespace, newNamespace);
55+
}
56+
}
57+
return count;
58+
}
59+
60+
// Process actual project
61+
if (!string.IsNullOrEmpty(project.FullName) && File.Exists(project.FullName))
62+
{
63+
var projectDirectory = Path.GetDirectoryName(project.FullName);
64+
if (!string.IsNullOrEmpty(projectDirectory) && Directory.Exists(projectDirectory))
65+
{
66+
return UpdateUsingStatementsInDirectory(projectDirectory, oldNamespace, newNamespace);
67+
}
68+
}
69+
70+
return 0;
71+
}
72+
73+
/// <summary>
74+
/// Updates using statements in all .cs files within a directory.
75+
/// </summary>
76+
private static int UpdateUsingStatementsInDirectory(string directory, string oldNamespace, string newNamespace)
77+
{
78+
var csFiles = Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories);
79+
var modifiedCount = 0;
80+
81+
foreach (var filePath in csFiles)
82+
{
83+
if (UpdateUsingStatementsInFile(filePath, oldNamespace, newNamespace))
84+
{
85+
modifiedCount++;
86+
}
87+
}
88+
89+
return modifiedCount;
90+
}
91+
92+
/// <summary>
93+
/// Updates using statements in a single source file.
94+
/// </summary>
95+
/// <param name="filePath">Full path to the .cs file.</param>
96+
/// <param name="oldNamespace">The old namespace to find.</param>
97+
/// <param name="newNamespace">The new namespace to replace with.</param>
98+
/// <returns>True if the file was modified, false otherwise.</returns>
99+
public static bool UpdateUsingStatementsInFile(string filePath, string oldNamespace, string newNamespace)
100+
{
101+
var encoding = DetectEncoding(filePath);
102+
var content = File.ReadAllText(filePath, encoding);
103+
var originalContent = content;
104+
105+
// Pattern for: using OldName;
106+
// Also handles nested: using OldName.SubNamespace;
107+
var usingPattern = $@"(\busing\s+){Regex.Escape(oldNamespace)}(\s*;|\.[\w.]*\s*;)";
108+
content = Regex.Replace(content, usingPattern, $"$1{newNamespace}$2");
109+
110+
// Pattern for using aliases: using Alias = OldName.Type;
111+
// Also handles: using Alias = OldName;
112+
var aliasPattern = $@"(\busing\s+\w+\s*=\s*){Regex.Escape(oldNamespace)}(\.[\w.]*)?(\s*;)";
113+
content = Regex.Replace(content, aliasPattern, $"$1{newNamespace}$2$3");
114+
115+
// Pattern for global using: global using OldName;
116+
var globalUsingPattern = $@"(\bglobal\s+using\s+){Regex.Escape(oldNamespace)}(\s*;|\.[\w.]*\s*;)";
117+
content = Regex.Replace(content, globalUsingPattern, $"$1{newNamespace}$2");
118+
119+
// Pattern for using static: using static OldName.ClassName;
120+
var staticUsingPattern = $@"(\busing\s+static\s+){Regex.Escape(oldNamespace)}(\.[\w.]+\s*;)";
121+
content = Regex.Replace(content, staticUsingPattern, $"$1{newNamespace}$2");
122+
123+
if (content != originalContent)
124+
{
125+
File.WriteAllText(filePath, content, encoding);
126+
return true;
127+
}
128+
129+
return false;
130+
}
131+
12132
/// <summary>
13133
/// Updates namespace declarations in all .cs files within the project directory.
14134
/// </summary>

0 commit comments

Comments
 (0)