OOP_2022/node_modules/git/lib/diff/hunk.js
2022-11-29 18:23:11 +01:00

292 lines
9.1 KiB
JavaScript

var Block = require('./block').Block;
// A Hunk is a group of Blocks which overlap because of the context
// surrounding each block. (So if we're not using context, every hunk will
// contain one block.) Used in the diff program (bin/diff).
var Hunk = exports.Hunk = function(data_old, data_new, piece, context, file_length_difference) {
// Internal variables
var _flag_context = null;
var self = this;
// At first, a hunk will have just one Block in it
this.blocks = [new Block(piece)];
this.data_old = data_old;
this.data_new = data_new;
var before = file_length_difference, after = file_length_difference;
after = after + this.blocks[0].diff_size;
this.file_length_difference = after; // The caller must get this manually
// Other parameters
var a1 = null, a2 = null;
var b1 = null, b2 = null;
// Save the start & end of each array. If the array doesn't exist
// (e.g., we're only adding items in this block), then figure out the
// line number based on the line number of the other file and the
// current difference in file lengths.
if(this.blocks[0].remove.length > 0) {
a1 = this.blocks[0].remove[0].position;
a2 = this.blocks[0].remove[this.blocks[0].remove.length - 1].position;
}
if(this.blocks[0].insert.length > 0) {
b1 = this.blocks[0].insert[0].position;
b2 = this.blocks[0].insert[this.blocks[0].insert.length - 1].position;
}
this.start_old = a1 || (b1 - before);
this.start_new = b1 || (a1 + before);
this.end_old = a2 || (b2 - after);
this.end_new = b2 || (a2 + after);
// Change the "start" and "end" fields to note that context should be added
// to this hunk
Object.defineProperty(this, "flag_context", { get: function() { return _flag_context; }, set: function(context) {
if(context == null || context == 0) return null;
var add_start = (context > self.start_old) ? self.start_old : context;
var add_end = null;
self.start_old = self.start_old - add_start;
self.start_new = self.start_new - add_start;
if((self.end_old + context) > self.data_old.length) {
add_end = self.data_old.length - self.end_old;
} else {
add_end = context;
}
self.end_old = self.end_old + add_end;
self.end_new = self.end_new + add_end;
_flag_context = context;
}, enumerable: true});
// Set the flag_context
this.flag_context = context;
}
Hunk.prototype.unshift = function(hunk) {
this.start_old = hunk.start_old;
this.start_new = hunk.start_new;
this.blocks = hunk.blocks.concat(this.blocks);
}
// Is there an overlap between hunk arg0 and old hunk arg1? Note: if end
// of old hunk is one less than beginning of second, they overlap
Hunk.prototype.overlaps = function(hunk) {
if(hunk == null) return null;
var a = (this.start_old - hunk.end_old) <= 1;
var b = (this.start_new - hunk.end_new) <= 1;
return (a || b);
}
Hunk.prototype.diff = function(format) {
if(format == "old") {
return old_diff(this);
} else if(format == 'unified') {
return unified_diff(this);
} else if(format == 'context') {
return context_diff(this);
} else if(format == 'ed') {
return this;
} else if(format == 'reverse_ed' || format == 'ed_finish') {
return ed_diff(this, format);
} else {
throw "unknown diff format " + format;
}
}
Hunk.prototype.each_old = function(block) {
var entries = this.data_old.slice(this.start_old, this.end_old);
entries.forEach(function(e) {
block(e);
});
}
// Note that an old diff can't have any context. Therefore, we know that
// there's only one block in the hunk.
var old_diff = function(hunk) {
if(hunk.blocks.length > 1) sys.puts("expecting only one block in an old diff hunk!");
// Set up operation actions
var opt_act = {'+':'a', '-':'d', '!':'c'};
var block = hunk.blocks[0];
// Calculate item number range. Old diff range is just like a context
// diff range, except the ranges are on one line with the action between
// them.
var s = "" + context_rang("old") + opt_act[block.op] + context_rang("new") + "\n";
// If removing anything, just print out all the remove lines in the hunk
// which is just all the remove lines in the block.
if(block.remove.length > 0) {
hunk.data_old.slice(hunk.start_old, hunk.end_old).forEach(function(e) {
s = s + "< " + e + "\n";
});
}
if(block.insert.length > 0) {
hunk.data_new.slice(hunk.start_new, hunk.end_new).forEach(function(e) {
s = s + "> " + e + "\n;"
});
}
// Return the diff string
return s;
}
var unified_diff = function(hunk) {
// Calculate item number range.
var s = "@@ -" + unified_range(hunk, 'old') + " +" + unified_range(hunk, 'new') + " @@\n";
// Outlist starts containing the hunk of the old file. Removing an item
// just means putting a '-' in front of it. Inserting an item requires
// getting it from the new file and splicing it in. We splice in
// +num_added+ items. Remove blocks use +num_added+ because splicing
// changed the length of outlist.
//
// We remove +num_removed+ items. Insert blocks use +num_removed+
// because their item numbers -- corresponding to positions in the NEW
// file -- don't take removed items into account.
var lo = hunk.start_old;
var hi = hunk.end_old;
var num_added = 0;
var num_removed = 0;
// Create list of stripped entries
var outlist = hunk.data_old.slice(lo, hi + 1).map(function(e) { return e.replace(/^/g, ' '); });
// Process all the blocks
hunk.blocks.forEach(function(block) {
block.remove.forEach(function(item) {
var op = item.action.toString(); // -
var offset = item.position - lo + num_added;
outlist[offset] = outlist[offset].replace(/^ /g, op.toString());
num_removed = num_removed + 1;
})
block.insert.forEach(function(item) {
var op = item.action.toString(); // +
var offset = item.position - hunk.start_new + num_removed;
outlist.splice(offset, 0, ("" + op + hunk.data_new[item.position]));
num_added = num_added + 1;
});
});
// Return the list
return s + outlist.join('\n');
}
var context_diff = function(hunk) {
var s = '***************\n';
s = s + '*** ' + context_range(hunk, 'old') + ' ****\n';
// Retrieve the context
var r = context_range(hunk, 'new');
var outlist = null;
// Print out file 1 part for each block in context diff format if there
// are any blocks that remove items
var lo = hunk.start_old;
var hi = hunk.end_old;
var removes = hunk.blocks.filter(function(e) { return !(e.remove.length == 0); });
if(removes) {
outlist = hunk.data_old.slice(lo, hi).map(function(e) { return e.replace(/^/g, ' '); });
removes.forEach(function(block) {
block.remove.forEach(function(item) {
outlist[item.position - lo] = outlist[item.position - lo].replace(/^ /g, block.op); // - or !
});
});
// Add to diff string
s = s + outlist.join('\n');
}
s = s + '\n-- ' + r + ' ----\n';
lo = hunk.start_new;
hi = hunk.end_new;
var inserts = hunk.blocks.filter(function(e) { return !(e.insert.length == 0); });
if(inserts) {
outlist = hunk.data_new.slice(lo, hi).map(function(e) { return e.replace(/^/g, ' '); });
inserts.forEach(function(block) {
block.insert.forEach(function(item) {
outlist[item.position - lo] = outlist[item.position - lo].replace(/^ /g, block.op); // + or !
});
});
// Add to diff string
s = s + outlist.join('\n');
}
// Return the diff string
return s;
}
var ed_diff = function(hunk, format) {
var opt_act = {'+':'a', '-':'d', '!':'c'};
if(hunk.blocks.length > 1) sys.puts("expecting only one block in an old diff hunk!");
var s = null;
if(format == 'reverse_ed') {
s = "" + op_act[hunk.blocks[0].op] + context_range(hunk, 'old') + '\n';
} else {
s = "" + context_range(hunk, 'old').replace(/,/g, ' ') + op_act[hunk.blocks[0].op] + '\n';
}
if(hunk.blocks[0].insert.length > 0) {
hunk.data_new.slice(hunk.start_new, hunk.end_new).forEach(function(e) {
s = s + '' + e + '\n';
});
// Add final marker
s = s + '.\n';
}
// Return diff string
return s;
}
// Generate a range of item numbers to print. Only print 1 number if the
// range has only one item in it. Otherwise, it's 'start,end'
var context_range = function(hunk, mode) {
var s = null, e = null;
if(mode == 'old') {
s = (hunk.start_old + 1);
e = (hunk.end_old + 1);
} else if(mode == 'new') {
s = (hunk.start_new + 1);
e = (hunk.end_new + 1);
}
return (s < e) ? ("" + s + "," + e) : ("" + e);
}
// Generate a range of item numbers to print for unified diff. Print
// number where block starts, followed by number of lines in the block
// (don't print number of lines if it's 1)
var unified_range = function(hunk, mode) {
var s = null, e = null;
if(mode == 'old') {
s = (hunk.start_old + 1);
e = (hunk.end_old + 1);
} else if(mode == 'new') {
s = (hunk.start_new + 1);
e = (hunk.end_new + 1);
}
var length = e - s + 1;
var first = (length < 2) ? e : s; // something weird
return (length == 1) ? ("" + first) : (first + "," + length);
}