4
4
import 'dart:io' ;
5
5
6
6
import 'package:collection/collection.dart' ;
7
+ import 'package:deviation/deviation.dart' ;
8
+ import 'package:deviation/unified_diff.dart' ;
7
9
import 'package:meta/meta.dart' ;
8
10
import 'package:path/path.dart' as path;
9
11
@@ -162,6 +164,26 @@ final class FileUpdater {
162
164
reportError (e.error);
163
165
}
164
166
167
+ Region ? diffWithRegion;
168
+ String ? diffWithRegionPath;
169
+ if (instruction.diffWith
170
+ case (path: final diffWithPath, region: final diffWithRegionName)) {
171
+ final combinedDiffWithPath = path.join (
172
+ baseSourcePath,
173
+ wholeFilePathBase,
174
+ diffWithPath,
175
+ );
176
+ try {
177
+ diffWithRegion = await extractor.extractRegion (
178
+ combinedDiffWithPath,
179
+ diffWithRegionName,
180
+ );
181
+ diffWithRegionPath = diffWithPath;
182
+ } on ExtractException catch (e) {
183
+ reportError (e.error);
184
+ }
185
+ }
186
+
165
187
var plaster = (instruction.plasterTemplate ?? wholeFilePlasterTemplate)
166
188
? .replaceAll (r'$defaultPlaster' , defaultPlasterContent);
167
189
@@ -173,6 +195,7 @@ final class FileUpdater {
173
195
}
174
196
175
197
var updatedLines = region.linesWithPlaster (plaster);
198
+ var updatedDiffLines = diffWithRegion? .linesWithPlaster (plaster);
176
199
177
200
final transforms = [
178
201
...instruction.transforms,
@@ -182,9 +205,38 @@ final class FileUpdater {
182
205
183
206
for (final transform in transforms) {
184
207
updatedLines = transform.transform (updatedLines);
208
+ if (updatedDiffLines != null ) {
209
+ updatedDiffLines = transform.transform (updatedDiffLines);
210
+ }
185
211
}
186
212
187
213
updatedLines = updatedLines.map ((line) => line.trimRight ());
214
+ updatedDiffLines = updatedDiffLines? .map ((line) => line.trimRight ());
215
+
216
+ if (updatedDiffLines != null ) {
217
+ final patch = _diffAlgorithm.compute <String >(
218
+ updatedLines.toList (growable: false ),
219
+ updatedDiffLines.toList (growable: false ),
220
+ );
221
+
222
+ final UnifiedDiffHeader diffHeader;
223
+ if (diffWithRegionPath != null ) {
224
+ diffHeader = UnifiedDiffHeader .custom (
225
+ sourceLineContent: instruction.targetPath,
226
+ targetLineContent: diffWithRegionPath,
227
+ );
228
+ } else {
229
+ diffHeader = const UnifiedDiffHeader .simple ();
230
+ }
231
+
232
+ final diff = UnifiedDiff .fromPatch (
233
+ patch,
234
+ header: diffHeader,
235
+ context: instruction.diffContext,
236
+ );
237
+
238
+ updatedLines = diff.toString ().trimRight ().split ('\n ' );
239
+ }
188
240
189
241
// Remove all shared whitespace on the left.
190
242
int ? sharedLeftWhitespace;
@@ -302,10 +354,16 @@ final class InjectionException implements Exception {
302
354
String toString () => '$filePath :$lineNumber - $error ' ;
303
355
}
304
356
357
+ const DiffAlgorithm _diffAlgorithm = DiffAlgorithm .myers ();
358
+
305
359
final RegExp _instructionPattern = RegExp (
306
360
r'^\s*<\?code-excerpt\s+(?:"(?<path>\S+)(?:\s\((?<region>[^)]+)\))?\s*")?(?<args>.*?)\?>$' ,
307
361
);
308
362
363
+ final RegExp _diffWithPattern = RegExp (
364
+ r'^(?<path>\S+)(?:\s\((?<region>[^)]+)\))?' ,
365
+ );
366
+
309
367
final RegExp _instructionStart = RegExp (r'^<\?code-excerpt' );
310
368
311
369
final RegExp _codeBlockStart =
@@ -394,6 +452,9 @@ final class _InjectInstruction extends _Instruction {
394
452
final String targetPath;
395
453
final String regionName;
396
454
455
+ final ({String path, String region})? diffWith;
456
+ final int diffContext;
457
+
397
458
final List <Transform > transforms;
398
459
399
460
final int ? indentBy;
@@ -405,6 +466,8 @@ final class _InjectInstruction extends _Instruction {
405
466
required this .transforms,
406
467
this .indentBy,
407
468
this .plasterTemplate,
469
+ this .diffWith,
470
+ this .diffContext = 3 ,
408
471
});
409
472
410
473
factory _InjectInstruction .fromArgs ({
@@ -415,6 +478,8 @@ final class _InjectInstruction extends _Instruction {
415
478
}) {
416
479
String ? indentByString;
417
480
String ? plasterTemplate;
481
+ String ? diffWithString;
482
+ String ? diffContextString;
418
483
419
484
final transforms = < Transform > [];
420
485
@@ -436,6 +501,22 @@ final class _InjectInstruction extends _Instruction {
436
501
);
437
502
}
438
503
plasterTemplate = argValue;
504
+ case 'diff-with' :
505
+ if (diffWithString != null ) {
506
+ reportError (
507
+ 'The `diff-with` argument can only be '
508
+ 'specified once per instruction.' ,
509
+ );
510
+ }
511
+ diffWithString = argValue;
512
+ case 'diff-u' :
513
+ if (diffContextString != null ) {
514
+ reportError (
515
+ 'The `diff-u` argument can only be '
516
+ 'specified once per instruction.' ,
517
+ );
518
+ }
519
+ diffContextString = argValue;
439
520
case 'skip' :
440
521
transforms.add (SkipTransform (int .parse (argValue)));
441
522
case 'take' :
@@ -458,17 +539,45 @@ final class _InjectInstruction extends _Instruction {
458
539
}
459
540
}
460
541
461
- final indentBy = indentByString == null ? null : int . parse (indentByString);
462
-
463
- if (indentBy != null && indentBy < 0 ) {
542
+ final indentBy =
543
+ indentByString == null ? null : int . tryParse (indentByString);
544
+ if (indentBy != null && indentBy < 1 ) {
464
545
reportError (
465
546
'The `indent-by` argument must be positive.' ,
466
547
);
467
548
}
468
549
550
+ final diffContext =
551
+ diffContextString == null ? null : int .tryParse (diffContextString);
552
+ if (diffContext != null && diffContext < 1 ) {
553
+ reportError (
554
+ 'The `diff-u` argument must be an integer greater than 1.' ,
555
+ );
556
+ }
557
+
558
+ ({String path, String region})? diffWith;
559
+
560
+ if (diffWithString != null ) {
561
+ final pathAndRegion = _diffWithPattern.firstMatch (diffWithString);
562
+ if (pathAndRegion == null ) {
563
+ reportError ('Invalid syntax for `diff-with` argument.' );
564
+ }
565
+
566
+ diffWith = (
567
+ path: pathAndRegion.namedGroup ('path' ) ?? '' ,
568
+ region: pathAndRegion.namedGroup ('region' ) ?? '' ,
569
+ );
570
+ } else if (diffContext != null ) {
571
+ reportError (
572
+ 'The `diff-u` argument must be specified with a `diff-with` argument.' ,
573
+ );
574
+ }
575
+
469
576
return _InjectInstruction (
470
577
targetPath: targetPath,
471
578
regionName: regionName,
579
+ diffWith: diffWith,
580
+ diffContext: diffContext ?? 3 ,
472
581
indentBy: indentBy,
473
582
plasterTemplate: plasterTemplate,
474
583
transforms: transforms,
0 commit comments