@fav/text.diff

Get diff between two texts.

Usage no npm install needed!

<script type="module">
  import favTextDiff from 'https://cdn.skypack.dev/@fav/text.diff';
</script>

README

@fav/text.diff NPM MIT License Build Status Build Status Coverage status

Get diff between two texts.

"fav" is an abbreviation of "favorite" and also the acronym of "for all versions". This package is intended to support all Node.js versions and many browsers as possible. At least, this package supports Node.js >= v0.10 and major Web browsers: Chrome, Firefox, IE11, Edge, Vivaldi and Safari.

Install

To install from npm:

$ npm install --save @fav/text.diff

NOTE: npm < 2.7.0 does not support scoped package, but even old version Node.js supports it. So when you use such older npm, you should download this package from github.com, and move it in node_modules/@fav/text.diff/ directory manually.

Usage

For Node.js:

var diff = require('@fav/text.diff');
diff('AB', 'ABC')
// => [{ type: 'a', src: { start: 2, end: 2 }, dest: { start: 2, end: 3 } }]
diff('ABC', 'BC')
// => [{ type: 'd', src: { start: 0, end: 1 }, dest: { start: 0, end: 0 } }]
diff('abc', 'adc')
// => [{ type: 'c', src: { start: 1, end: 2 }, dest: { start: 1, end: 2 } }]

For Web browsers:

<script src="fav.text.diff.min.js"></script>
<script>
var diff = fav.text.diff;
diff('AB', 'ABC')
// => [{ type: 'a', src: { start: 2, end: 2 }, dest: { start: 2, end: 3 } }]
diff('ABC', 'BC')
// => [{ type: 'd', src: { start: 0, end: 1 }, dest: { start: 0, end: 0 } }]
diff('abc', 'adc')
// => [{ type: 'c', src: { start: 1, end: 2 }, dest: { start: 1, end: 2 } }]
</script>

API

diff(srcText, destText [, opts]) => Array

Get differenece of two texts. The result is an array of edit infos from srcText to destText.

Parameter:

Parameter Type Description
srcText string A text before editing.
destText string A text after editing.
opts object comparing options.

The comparing options are as follows:

Option Type Description
delimRe RegExp A regular expression to split text blocks. If this option is not specified, this function splits and compares by characters.
ignoreCase boolean If true, this function compares text blocks with ignoring upper/lower case.
normalizeSpaces boolean If true, this function compares text blocks with replacing continuous whitespaces included them to one whitespace.

Return:

The array of edit informations. The edit info is an object of which properties is as follows:

Property Description
type The edit type. This property can have following values: 'a' (Adding), 'd' (Deleting), and 'c' (Changing).
src The range of indexies in srcText to be editted. This is an object which has two indexes: start and end.
dest The range of indexies in destText to be editted. This is an object which has two indexes: start and end.

diff.lines(srcText, destText [, opts]) => Array

Get difference of two texts by line. The result is an array of edit infos from srcText to destText.

Parameter Type Description
srcText string A text before editing.
destText string A text after editing.
opts object comparing options.

The comparing options are as follows:

Option Type Description
ignoreCase boolean If true, this function compares text blocks with ignoring upper/lower case.
normalizeSpaces boolean If true, this function compares text blocks with replacing continuous whitespaces included them to one whitespace.

Return:

The array of edit informations. The edit info is an object of which properties is as follows:

Property Description
type The edit type. This property can have following values: 'a' (Adding), 'd' (Deleting), and 'c' (Changing).
src The range of indexies in srcText to be editted. This is an object which has two indexes: start and end.
dest The range of indexies in destText to be editted. This is an object which has two indexes: start and end.
lines.src The range of line indexes in srcText to be editted. This is an object which has two line indexes: start and end.
lines.dest The range of line indexes in destText to be editted. This is an object which has two line indexes: start and end.

The line index is zero-based. The ways to get lines to be editted are as follows:

const srcText = 'aaa\nbbb\nccc\nddd';
const dstText = 'aaa\nbb\ncccc\ne\nddd';
diff.lines(srcText, dstText).forEach(d => {
  const src = srcText.slice(d.src.start, d.src.end);
  const dst = dstText.slice(d.dest.start, d.dest.end);

  if (src) console.log('< ' + src.replace(/\n/g, '\n< '));
  if (src && dst) console.log('---');
  if (dst) console.log('> ' + dst.replace(/\n/g, '\n> '));
});
// => < bbb
//    < ccc
//    ---
//    > bb
//    > cccc
//    > e

or

const srcText = 'aaa\nbbb\nccc\nddd';
const dstText = 'aaa\nbb\ncccc\ne\nddd';
diff.lines(srcText, dstText).forEach(d => {
  const srcLines = srcText.split('\n')
                          .slice(d.lines.src.start, d.lines.src.end);
  const dstLines = dstText.split('\n')
                          .slice(d.lines.dest.start, d.lines.dest.end);

  console.log(srcLines.map(line => '< ' + line).join('\n'));
  if (srcLines.length && dstLines.length) console.log('---');
  console.log(dstLines.map(line => '> ' + line).join('\n'));
});
// => < bbb
//    < ccc
//    ---
//    > bb
//    > cccc
//    > e

The way to get line numbers as in diff normal format is as follows:

const srcText = 'aaa\nbbb\nccc\nddd';
const dstText = 'aaa\nbb\ncccc\ne\nddd';
function getLineNo(range) {
  if (range.start === range.end) return range.start;  // Minus line index
  
  var st = range.start + 1;  // Plus start line index to start line No.
  var ed = range.end;        // Plus end line index to last line No.
  if (st === ed) return st;
  return st + ',' + ed;
}
diff.lines(srcText, dstText).forEach(d => {
  console.log(getLineNo(d.lines.src) + d.type + getLineNo(d.lines.dest));
});
// => 2,3c2,4

Checked

Node.js (4〜12)

Platform 11 12
macOS
Windows10
Linux
Platform 4 5 6 7 8 9 10
macOS
Windows10
Linux

io.js (1〜3)

Platform 1 2 3
macOS
Windows10
Linux

Node.js (〜0.12)

Platform 0.8 0.9 0.10 0.11 0.12
macOS
Windows10
Linux

Web browsers

Platform Chrome Firefox Vivaldi Safari Edge IE11
macOS -- --
Windows10 --
Linux -- -- --

License

Copyright (C) 2019 Takayuki Sato

This program is free software under MIT License. See the file LICENSE in this distribution for more details.