mirror of
https://github.com/zulip/zulip.git
synced 2025-11-01 12:33:40 +00:00
ES and TypeScript modules are strict by default and don’t need this directive. ESLint will remind us to add it to new CommonJS files and remove it from ES and TypeScript modules. Signed-off-by: Anders Kaseorg <anders@zulip.com>
152 lines
4.5 KiB
JavaScript
152 lines
4.5 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* mdiff.js
|
|
*
|
|
* Used to produce colorful and informative diffs for comparison of generated
|
|
* Markdown. Unlike the built-in diffs used in python or node.js assert libraries,
|
|
* is actually designed to be effective for long, single-line comparisons.
|
|
*
|
|
* Based on diffing library difflib, a js port of the python library.
|
|
*
|
|
* The sole exported function diff_strings(string_0, string_1) returns a pretty-printed
|
|
* unicode string containing their diff.
|
|
*/
|
|
|
|
const difflib = require("difflib");
|
|
|
|
function apply_color(input_string, changes) {
|
|
let previous_index = 0;
|
|
let processed_string = input_string.slice(0, 2);
|
|
input_string = input_string.slice(2);
|
|
|
|
const formatter = new Map([
|
|
["delete", (string) => "\u001b[31m" + string + "\u001b[0m"],
|
|
["insert", (string) => "\u001b[32m" + string + "\u001b[0m"],
|
|
["replace", (string) => "\u001b[33m" + string + "\u001b[0m"],
|
|
]);
|
|
changes.forEach((change) => {
|
|
if (formatter.has(change.tag)) {
|
|
processed_string += input_string.slice(previous_index, change.beginning_index);
|
|
processed_string += formatter.get(change.tag)(
|
|
input_string.slice(change.beginning_index, change.ending_index),
|
|
);
|
|
previous_index = change.ending_index;
|
|
}
|
|
});
|
|
|
|
processed_string += input_string.slice(previous_index);
|
|
return processed_string;
|
|
}
|
|
|
|
/**
|
|
* The library difflib produces diffs that look as follows:
|
|
*
|
|
* - <p>upgrade! yes</p>
|
|
* ? ^^ -
|
|
* + <p>downgrade yes.</p>
|
|
* ? ^^^^ +
|
|
*
|
|
* The purpose of this function is to facilitate converting these diffs into
|
|
* colored versions, where the question-mark lines are removed, replaced with
|
|
* directions to add appropriate color to the lines that they annotate.
|
|
*/
|
|
function parse_questionmark_line(questionmark_line) {
|
|
let current_sequence = ""; // Either "^", "-", "+", or ""
|
|
let beginning_index = 0;
|
|
let index = 0;
|
|
|
|
const changes_list = [];
|
|
const aliases = new Map([
|
|
["^", "replace"],
|
|
["+", "insert"],
|
|
["-", "delete"],
|
|
]);
|
|
const add_change = () => {
|
|
if (current_sequence) {
|
|
changes_list.push({
|
|
tag: aliases.get(current_sequence),
|
|
beginning_index,
|
|
ending_index: index,
|
|
});
|
|
current_sequence = "";
|
|
}
|
|
};
|
|
|
|
questionmark_line = questionmark_line.slice(2).trimRight("\n");
|
|
|
|
for (const character of questionmark_line) {
|
|
if (aliases.has(character)) {
|
|
if (current_sequence !== character) {
|
|
add_change();
|
|
current_sequence = character;
|
|
beginning_index = index;
|
|
}
|
|
} else {
|
|
add_change();
|
|
}
|
|
index += 1;
|
|
}
|
|
|
|
// In case we have a "change" involving the last character on a line
|
|
// e.g. a string such as "? ^^ -- ++++"
|
|
add_change();
|
|
|
|
return changes_list;
|
|
}
|
|
|
|
function diff_strings(string_0, string_1) {
|
|
let output_lines = [];
|
|
let ndiff_output = "";
|
|
let changes_list = [];
|
|
|
|
ndiff_output = difflib.ndiff(string_0.split("\n"), string_1.split("\n"));
|
|
|
|
ndiff_output.forEach((line) => {
|
|
if (line.startsWith("+")) {
|
|
output_lines.push(line);
|
|
} else if (line.startsWith("-")) {
|
|
output_lines.push(line);
|
|
} else if (line.startsWith("?")) {
|
|
changes_list = parse_questionmark_line(line);
|
|
output_lines[output_lines.length - 1] = apply_color(
|
|
output_lines[output_lines.length - 1],
|
|
changes_list,
|
|
);
|
|
} else {
|
|
output_lines.push(line);
|
|
}
|
|
});
|
|
|
|
const emphasize_codes = (string) =>
|
|
"\u001b[34m" + string.slice(0, 1) + "\u001b[0m" + string.slice(1);
|
|
output_lines = output_lines.map(emphasize_codes);
|
|
|
|
return output_lines.join("\n");
|
|
}
|
|
|
|
module.exports = {diff_strings};
|
|
|
|
// Simple CLI for this module
|
|
// Only run this code if called as a command-line utility
|
|
if (require.main === module) {
|
|
// First two args are just "node" and "mdiff.js"
|
|
const argv = require("minimist")(process.argv.slice(2));
|
|
|
|
if (Object.prototype.hasOwnProperty.call(argv, "help")) {
|
|
console.log(
|
|
process.argv[0] +
|
|
" " +
|
|
process.argv[1] +
|
|
" [ --help ]" +
|
|
" string_0" +
|
|
" string_1" +
|
|
"\n" +
|
|
"Where string_0 and string_1 are the strings to be diffed",
|
|
);
|
|
}
|
|
|
|
const output = diff_strings(argv._[0], argv._[1]);
|
|
console.log(output);
|
|
}
|