@@ -26,7 +26,6 @@ export type Effect =
2626 type : "replace"
2727 path : string
2828 lines : string [ ]
29- noNewlineAtEndOfFile : boolean
3029 }
3130
3231export const executeEffects = ( effects : Effect [ ] ) => {
@@ -47,10 +46,7 @@ export const executeEffects = (effects: Effect[]) => {
4746 )
4847 break
4948 case "replace" :
50- fs . writeFileSync (
51- eff . path ,
52- eff . lines . join ( "\n" ) + ( eff . noNewlineAtEndOfFile ? "" : "\n" ) ,
53- )
49+ fs . writeFileSync ( eff . path , eff . lines . join ( "\n" ) )
5450 break
5551 }
5652 } )
@@ -70,16 +66,33 @@ function assertLineEquality(onDisk: string, expected: string) {
7066 }
7167}
7268
69+ /**
70+ * How does noNewLineAtEndOfFile work?
71+ *
72+ * if you remove the newline from a file that had one without editing other bits:
73+ *
74+ * it creates an insertion/removal pair where the insertion has \ No new line at end of file
75+ *
76+ * if you edit a file that didn't have a new line and don't add one:
77+ *
78+ * both insertion and deletion have \ No new line at end of file
79+ *
80+ * if you edit a file that didn't have a new line and add one:
81+ *
82+ * deletion has \ No new line at end of file
83+ * but not insertion
84+ *
85+ * if you edit a file that had a new line and leave it in:
86+ *
87+ * neither insetion nor deletion have the annoation
88+ *
89+ */
90+
7391function applyPatch ( { parts, path } : FilePatch ) : Effect {
7492 // modifying the file in place
7593 const fileContents = fs . readFileSync ( path ) . toString ( )
7694
7795 const fileLines : string [ ] = fileContents . split ( / \n / )
78- if ( fileLines [ fileLines . length - 1 ] === "" ) {
79- fileLines . pop ( )
80- }
81-
82- let noNewlineAtEndOfFile = true
8396
8497 // when adding or removing lines from a file, gotta
8598 // make sure that the original lines in hunk headers match up
@@ -117,30 +130,19 @@ function applyPatch({ parts, path }: FilePatch): Effect {
117130 )
118131 contextIndex -= part . lines . length
119132
120- if ( contextIndex >= fileLines . length ) {
121- if (
122- ( hunkHeader . patched . length > 0 && part . noNewlineAtEndOfFile ) ||
123- ( parts [ i - 2 ] &&
124- ( parts [ i - 2 ] . type === "insertion" ||
125- parts [ i - 2 ] . type === "context" ) &&
126- ! ( parts [ i - 2 ] as any ) . noNewlineAtEndOfFile )
127- ) {
128- // delete the fact that there was no newline at the end of the file
129- // by adding a newline to the end of the file
130- noNewlineAtEndOfFile = false
131- }
132- }
133- } else {
134- if ( contextIndex >= fileLines . length ) {
135- noNewlineAtEndOfFile = part . noNewlineAtEndOfFile
133+ if ( part . noNewlineAtEndOfFile ) {
134+ fileLines . push ( "" )
136135 }
137136 }
138137 break
139138 case "insertion" :
140139 fileLines . splice ( contextIndex , 0 , ...part . lines )
141140 contextIndex += part . lines . length
142- if ( contextIndex >= fileLines . length ) {
143- noNewlineAtEndOfFile = part . noNewlineAtEndOfFile
141+ if ( part . noNewlineAtEndOfFile ) {
142+ if ( contextIndex !== fileLines . length - 1 ) {
143+ throw new Error ( "Invalid patch application state." )
144+ }
145+ fileLines . pop ( )
144146 }
145147 break
146148 }
@@ -149,7 +151,6 @@ function applyPatch({ parts, path }: FilePatch): Effect {
149151
150152 return {
151153 type : "replace" ,
152- noNewlineAtEndOfFile,
153154 path,
154155 lines : fileLines ,
155156 }
0 commit comments