@@ -80,6 +80,7 @@ private void RenameProject(Project project, DTE2 dte)
8080
8181 var newName = dialog . NewProjectName ;
8282 var projectFilePath = project . FullName ;
83+ var originalProjectFilePath = projectFilePath ;
8384
8485 // Show progress dialog
8586 var progressDialog = new RenameProgressDialog ( currentName ) ;
@@ -90,77 +91,116 @@ private void RenameProject(Project project, DTE2 dte)
9091 progressDialog . Show ( ) ;
9192
9293 var stepIndex = 0 ;
94+ var projectRemovedFromSolution = false ;
95+ var projectReaddedToSolution = false ;
96+ System . Collections . Generic . List < string > referencingProjects = null ;
97+ Project parentSolutionFolder = null ;
9398
94- // Step 1: Collect projects that reference this project before removal
95- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
99+ try
96100 {
97- return ProjectReferenceService . FindProjectsReferencingTarget ( dte . Solution , projectFilePath ) ;
98- } , out var referencingProjects ) ;
99- var oldProjectFilePath = projectFilePath ;
101+ // Step 1: Collect projects that reference this project before removal
102+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
103+ {
104+ return ProjectReferenceService . FindProjectsReferencingTarget ( dte . Solution , projectFilePath ) ;
105+ } , out referencingProjects ) ;
106+ var oldProjectFilePath = projectFilePath ;
100107
101- // Step 2: Capture the parent solution folder before removal
102- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
103- {
104- return SolutionFolderService . GetParentSolutionFolder ( project ) ;
105- } , out var parentSolutionFolder ) ;
108+ // Step 2: Capture the parent solution folder before removal
109+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
110+ {
111+ return SolutionFolderService . GetParentSolutionFolder ( project ) ;
112+ } , out parentSolutionFolder ) ;
106113
107- // Step 3: Remove project from solution before file operations
108- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
109- {
110- dte . Solution . Remove ( project ) ;
111- } ) ;
114+ // Step 3: Remove project from solution before file operations
115+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
116+ {
117+ dte . Solution . Remove ( project ) ;
118+ projectRemovedFromSolution = true ;
119+ } ) ;
112120
113- // Step 4: Update RootNamespace and AssemblyName in .csproj
114- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
115- {
116- ProjectFileService . UpdateProjectFile ( projectFilePath , currentName , newName ) ;
117- } ) ;
121+ // Step 4: Update RootNamespace and AssemblyName in .csproj
122+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
123+ {
124+ ProjectFileService . UpdateProjectFile ( projectFilePath , currentName , newName ) ;
125+ } ) ;
118126
119- // Step 5: Update namespace declarations in source files
120- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
121- {
122- SourceFileService . UpdateNamespacesInProject ( projectFilePath , currentName , newName ) ;
123- } ) ;
127+ // Step 5: Update namespace declarations in source files
128+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
129+ {
130+ SourceFileService . UpdateNamespacesInProject ( projectFilePath , currentName , newName ) ;
131+ } ) ;
124132
125- // Step 6: Rename the project file on disk
126- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
127- {
128- return ProjectFileService . RenameProjectFile ( projectFilePath , newName ) ;
129- } , out projectFilePath ) ;
133+ // Step 6: Rename the project file on disk
134+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
135+ {
136+ return ProjectFileService . RenameProjectFile ( projectFilePath , newName ) ;
137+ } , out projectFilePath ) ;
130138
131- // Step 7: Rename parent directory if it matches the old project name
132- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
133- {
134- return ProjectFileService . RenameParentDirectoryIfMatches ( projectFilePath , currentName , newName ) ;
135- } , out projectFilePath ) ;
139+ // Step 7: Rename parent directory if it matches the old project name
140+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
141+ {
142+ return ProjectFileService . RenameParentDirectoryIfMatches ( projectFilePath , currentName , newName ) ;
143+ } , out projectFilePath ) ;
136144
137- // Step 8: Update references in projects that referenced this project
138- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
139- {
140- ProjectReferenceService . UpdateProjectReferences ( referencingProjects , oldProjectFilePath , projectFilePath ) ;
141- } ) ;
145+ // Step 8: Update references in projects that referenced this project
146+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
147+ {
148+ ProjectReferenceService . UpdateProjectReferences ( referencingProjects , oldProjectFilePath , projectFilePath ) ;
149+ } ) ;
142150
143- // Step 9: Re-add project to solution, preserving solution folder location
144- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
145- {
146- SolutionFolderService . AddProjectToSolution ( dte . Solution , projectFilePath , parentSolutionFolder ) ;
147- } ) ;
151+ // Step 9: Re-add project to solution, preserving solution folder location
152+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
153+ {
154+ SolutionFolderService . AddProjectToSolution ( dte . Solution , projectFilePath , parentSolutionFolder ) ;
155+ projectReaddedToSolution = true ;
156+ } ) ;
148157
149- // Step 10: Update using statements across the entire solution
150- ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
158+ // Step 10: Update using statements across the entire solution
159+ ExecuteStep ( progressDialog , stepIndex ++ , ( ) =>
160+ {
161+ SourceFileService . UpdateUsingStatementsInSolution ( dte . Solution , currentName , newName ) ;
162+ } ) ;
163+
164+ // Mark as complete and close after a brief delay
165+ progressDialog . Complete ( ) ;
166+ DoEvents ( ) ;
167+ System . Threading . Thread . Sleep ( 500 ) ;
168+ progressDialog . Close ( ) ;
169+ }
170+ catch ( Exception ex )
151171 {
152- SourceFileService . UpdateUsingStatementsInSolution ( dte . Solution , currentName , newName ) ;
153- } ) ;
172+ // Mark the current step as failed
173+ progressDialog . FailStep ( stepIndex , ex . Message ) ;
174+ DoEvents ( ) ;
154175
155- // Mark as complete and close after a brief delay
156- progressDialog . Complete ( ) ;
157- DoEvents ( ) ;
158- System . Threading . Thread . Sleep ( 500 ) ;
159- progressDialog . Close ( ) ;
176+ // Attempt rollback if project was removed but not re-added
177+ if ( projectRemovedFromSolution && ! projectReaddedToSolution )
178+ {
179+ try
180+ {
181+ // Try to re-add the project at its current location
182+ var currentProjectPath = File . Exists ( projectFilePath ) ? projectFilePath : originalProjectFilePath ;
183+ if ( File . Exists ( currentProjectPath ) )
184+ {
185+ SolutionFolderService . AddProjectToSolution ( dte . Solution , currentProjectPath , parentSolutionFolder ) ;
186+ }
187+ }
188+ catch
189+ {
190+ // Rollback failed, nothing more we can do
191+ }
192+ }
193+
194+ // Show error message
195+ System . Windows . MessageBox . Show (
196+ $ "An error occurred while renaming the project:\n \n { ex . Message } \n \n " +
197+ "The operation has been aborted. The project may be in a partially renamed state." ,
198+ "Rename Failed" ,
199+ System . Windows . MessageBoxButton . OK ,
200+ System . Windows . MessageBoxImage . Error ) ;
160201
161- // TODO: Implement remaining rename operations
162- // See open issues for requirements:
163- // - #13: Error handling and rollback
202+ progressDialog . Close ( ) ;
203+ }
164204 }
165205
166206 private void ExecuteStep ( RenameProgressDialog dialog , int stepIndex , Action action )
0 commit comments