6
6
import AdmZip from 'adm-zip'
7
7
import os from 'os'
8
8
import fs from 'fs-extra'
9
- import parseDiff from 'parse- diff'
9
+ import { parsePatch , applyPatches , ParsedDiff } from 'diff'
10
10
import path from 'path'
11
11
import vscode from 'vscode'
12
12
@@ -102,31 +102,6 @@ export class AddedChangeNode extends ProposedChangeNode {
102
102
}
103
103
}
104
104
105
- export class RemovedChangeNode extends ProposedChangeNode {
106
- readonly pathToOldContents : string
107
- override resourcePath : string
108
-
109
- constructor ( pathToOldContents : string ) {
110
- super ( )
111
- this . pathToOldContents = pathToOldContents
112
- this . resourcePath = pathToOldContents
113
- }
114
-
115
- override generateCommand ( ) : vscode . Command {
116
- return {
117
- command : 'vscode.open' ,
118
- arguments : [ vscode . Uri . file ( this . pathToOldContents ) ] ,
119
- title : 'Removed Change' ,
120
- }
121
- }
122
- override generateDescription ( ) : string {
123
- return 'R'
124
- }
125
- override saveFile ( ) : void {
126
- fs . removeSync ( this . pathToOldContents )
127
- }
128
- }
129
-
130
105
enum ReviewState {
131
106
ToReview ,
132
107
Reviewed_Accepted ,
@@ -137,19 +112,74 @@ export class DiffModel {
137
112
changes : ProposedChangeNode [ ] = [ ]
138
113
139
114
/**
140
- *
115
+ * This function creates a copy of the changed files of the user's project so that the diff.patch can be applied to them
116
+ * @param pathToWorkspace Path to the project that was transformed
117
+ * @param changedFiles List of files that were changed
118
+ * @returns Path to the folder containing the copied files
119
+ */
120
+ public copyProject ( pathToWorkspace : string , changedFiles : ParsedDiff [ ] ) {
121
+ const pathToTmpSrcDir = path . join ( os . tmpdir ( ) , `project-copy-${ Date . now ( ) } ` )
122
+ fs . mkdirSync ( pathToTmpSrcDir )
123
+ changedFiles . forEach ( file => {
124
+ const pathToTmpFile = path . join ( pathToTmpSrcDir , file . oldFileName ! . substring ( 2 ) )
125
+ // use mkdirsSync to create parent directories in pathToTmpFile too
126
+ fs . mkdirsSync ( path . dirname ( pathToTmpFile ) )
127
+ const pathToOldFile = path . join ( pathToWorkspace , file . oldFileName ! . substring ( 2 ) )
128
+ // pathToOldFile will not exist for new files such as summary.md
129
+ if ( fs . existsSync ( pathToOldFile ) ) {
130
+ fs . copyFileSync ( pathToOldFile , pathToTmpFile )
131
+ }
132
+ } )
133
+ return pathToTmpSrcDir
134
+ }
135
+
136
+ /**
141
137
* @param pathToDiff Path to the diff.patch file expected to be located in the archive returned by ExportResultsArchive
142
- * @param pathToTmpSrcDir Path to the directory containing changed source files
143
- * @param pathToWorkspace Path to the current open workspace directory
144
- * @returns
138
+ * @param pathToWorkspace Path to the project that was transformed
139
+ * @returns List of nodes containing the paths of files that were modified, added, or removed
145
140
*/
146
- public parseDiff ( pathToDiff : string , pathToTmpSrcDir : string , pathToWorkspace : string ) : ProposedChangeNode [ ] {
141
+ public parseDiff ( pathToDiff : string , pathToWorkspace : string ) : ProposedChangeNode [ ] {
147
142
const diffContents = fs . readFileSync ( pathToDiff , 'utf8' )
148
- const changedFiles = parseDiff ( diffContents )
149
-
143
+ const changedFiles = parsePatch ( diffContents )
144
+ // path to the directory containing copy of the changed files in the transformed project
145
+ const pathToTmpSrcDir = this . copyProject ( pathToWorkspace , changedFiles )
146
+ transformByQState . setProjectCopyFilePath ( pathToTmpSrcDir )
147
+
148
+ applyPatches ( changedFiles , {
149
+ loadFile : function ( fileObj , callback ) {
150
+ // load original contents of file
151
+ const filePath = path . join ( pathToWorkspace , fileObj . oldFileName ! . substring ( 2 ) )
152
+ if ( ! fs . existsSync ( filePath ) ) {
153
+ // must be a new file (ex. summary.md), so pass empty string as original contents and do not pass error
154
+ callback ( undefined , '' )
155
+ } else {
156
+ // must be a modified file (most common), so pass original contents
157
+ const fileContents = fs . readFileSync ( filePath , 'utf-8' )
158
+ callback ( undefined , fileContents )
159
+ }
160
+ } ,
161
+ // by now, 'content' contains the changes from the patch
162
+ patched : function ( fileObj , content , callback ) {
163
+ const filePath = path . join ( pathToTmpSrcDir , fileObj . newFileName ! . substring ( 2 ) )
164
+ // write changed contents to the copy of the original file (or create a new file)
165
+ fs . writeFileSync ( filePath , content )
166
+ callback ( undefined )
167
+ } ,
168
+ complete : function ( err ) {
169
+ if ( err ) {
170
+ getLogger ( ) . error ( `CodeTransform: ${ err } when applying patch` )
171
+ } else {
172
+ getLogger ( ) . info ( 'CodeTransform: Patch applied successfully' )
173
+ }
174
+ } ,
175
+ } )
150
176
this . changes = changedFiles . flatMap ( file => {
151
- const originalPath = path . join ( pathToWorkspace , file . from !== undefined ? file . from : '' )
152
- const tmpChangedPath = path . join ( pathToTmpSrcDir , file . to !== undefined ? file . to : '' )
177
+ /* ex. file.oldFileName = 'a/src/java/com/project/component/MyFile.java'
178
+ * ex. file.newFileName = 'b/src/java/com/project/component/MyFile.java'
179
+ * use substring(2) to ignore the 'a/' and 'b/'
180
+ */
181
+ const originalPath = path . join ( pathToWorkspace , file . oldFileName ! . substring ( 2 ) )
182
+ const tmpChangedPath = path . join ( pathToTmpSrcDir , file . newFileName ! . substring ( 2 ) )
153
183
154
184
const originalFileExists = fs . existsSync ( originalPath )
155
185
const changedFileExists = fs . existsSync ( tmpChangedPath )
@@ -158,8 +188,6 @@ export class DiffModel {
158
188
return new ModifiedChangeNode ( originalPath , tmpChangedPath )
159
189
} else if ( ! originalFileExists && changedFileExists ) {
160
190
return new AddedChangeNode ( originalPath , tmpChangedPath )
161
- } else if ( originalFileExists && ! changedFileExists ) {
162
- return new RemovedChangeNode ( originalPath )
163
191
}
164
192
return [ ]
165
193
} )
@@ -322,7 +350,6 @@ export class ProposedTransformationExplorer {
322
350
zip . extractAllTo ( pathContainingArchive )
323
351
diffModel . parseDiff (
324
352
path . join ( pathContainingArchive , ExportResultArchiveStructure . PathToDiffPatch ) ,
325
- path . join ( pathContainingArchive , ExportResultArchiveStructure . PathToSourceDir ) ,
326
353
transformByQState . getProjectPath ( )
327
354
)
328
355
await vscode . commands . executeCommand (
@@ -373,8 +400,9 @@ export class ProposedTransformationExplorer {
373
400
await vscode . commands . executeCommand ( 'setContext' , 'gumby.reviewState' , TransformByQReviewStatus . NotStarted )
374
401
transformDataProvider . refresh ( )
375
402
await vscode . window . showInformationMessage ( CodeWhispererConstants . changesAppliedMessage )
376
- // delete result archive after changes accepted
403
+ // delete result archive and copied source code after changes accepted
377
404
fs . rmSync ( transformByQState . getResultArchiveFilePath ( ) , { recursive : true , force : true } )
405
+ fs . rmSync ( transformByQState . getProjectCopyFilePath ( ) , { recursive : true , force : true } )
378
406
telemetry . codeTransform_vcsViewerSubmitted . emit ( {
379
407
codeTransformSessionId : codeTransformTelemetryState . getSessionId ( ) ,
380
408
codeTransformJobId : transformByQState . getJobId ( ) ,
0 commit comments