寻求支持交互式操作的Visual Diff Patch Editor工具
寻找交互式可视化Diff补丁编辑器
我之前在Software Recommendations板块问过关于可视化Diff补丁编辑器的问题,但没拿到想要的代码示例。现在我需要一款交互式的同类工具,核心需求如下:
- 支持直接编辑补丁内容,或者编辑左右对比面板里的代码内容
- 能实时查看修改后的Diff结果,也可以执行补丁回退操作
- 理想状态下,能像Github Pull Requests那样,支持同时编辑PR里的所有文件
我自己实现的两个相关工具
1. 基于diff2html的可视化Diff展示与提取工具
这个工具借助diff2html实现Diff的可视化展示,还能提取左右面板的内容到文本框中,代码如下:
<head> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/github.min.css" /> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" /> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script> </head> <script> var tas = document.getElementsByTagName("TEXTAREA"); function show(diffString) { var targetElement = document.getElementById('myDiffElement'); var configuration = { drawFileList: false, fileListToggle: false, fileListStartVisible: false, fileContentToggle: false, matching: 'lines', outputFormat: 'side-by-side', synchronisedScroll: true, highlight: true, renderNothingWhenEmpty: false, }; var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration); diff2htmlUi.draw(); diff2htmlUi.highlightCode(); var tabs = document.getElementsByTagName("TABLE"); tas[1].value = printTab(tabs[0]); tas[2].value = printTab(tabs[1]); } function printTab(t) { var o = ""; for (var r = 1; r < t.rows.length; r++) { var cs = []; for (var c = 1; c < t.rows[r].cells.length; c++) { cs.push(t.rows[r].cells[c].innerText); } o += cs.join('\t') + '\n'; } return o; } function demo() { show(tas[0].value); } setTimeout(demo, 2000) </script> <body onload=""> <textarea onchange="show(this.value)" rows="4" cols="20">diff --git a/https://codepen.io/HatScripts/full/QvWVgP b/https://codepen.io/HatScripts/full/QvWVgP @@ -1,3 +1,3 @@ function example() --- a/https://codepen.io/HatScripts/full/QvWVgP +++ b/https://codepen.io/HatScripts/full/QvWVgP function example() { - return "pizza"; + return "pasta"; }</textarea> <textarea rows="4" cols="25"></textarea> <textarea rows="4" cols="25"></textarea> <div id="myDiffElement"></div> </body>
2. 补丁文件转换与过滤工具
这个工具可以统一缩减空白字符、强制转为小写后再进行对比,不过我不确定它能不能处理所有类型的补丁文件,代码如下:
<body onload="init()"> <textarea ondrop="droppper(event)" rows=60 cols=120 onchange="process(this.value)"></textarea> <textarea rows=60 cols=120></textarea> </body> <script> var ta; function transformLeft(i) { return i.toLocaleLowerCase().replace(/\s+/g, ' '); } function transformRight(i) { return i.toLocaleLowerCase().replace(/\s+/g, ' '); } function process(t) { var res = [], head = { prev:[], now:[], tl:[], tr:[], tempRows:[] }, processed = []; var lastDiff = null; var r = t.split('\n'); if (!r[r.length]) r.pop(); var counts = null; for(var i of r) { var type = null; if (i.substr(0,5) == 'diff ') { head = { prev:[], now:[], tl:[], tr:[], tempRows:[] }; counts = null; type = 'diff'; } else if (i.substr(0,6) == 'index ') type = 'index'; else if (i.substr(0,4) == '--- ') type = '---'; else if (i.substr(0,4) == '+++ ') type = '+++'; else if (i.substr(0,3) == '@@ ') { type = '@@'; counts = i.substr(3, i.length - 6).split(' '); if (!head.lines || head['@@'] != i) { head.lines = { prev: parseInt(counts[0].replace(/\.+,/, '').replace(/[+-]/, '')), now: parseInt(counts[1].replace(/\.+,/, '').replace(/[+-]/, '')) }; } counts = i; continue; } if (type && !head[type]) { head[type] = i; continue; } if (type == null && !head.lines) { var ignored = { "copy from ":2, "copy to ":2, "new mode ":2, "old mode ":2, "rename from ":2, "rename to ":2, "similarity index ":2, "dissimilarity index ":3, "deleted file mode ":3, "new file mode ":3 }; var space = i.indexOf(' '); if (space > 0) { space++; if (ignored[i.substr(0, space)] == 1) { continue; } space = i.indexOf(' ', space); space++; if (ignored[i.substr(0, space)] == 2) { continue; } space = i.indexOf(' ', space); space++; if (ignored[i.substr(0, space)] == 3) { continue; } } alert("Unexpected content " + i + " !"); } head.tempRows.push(i); var diff = 0; if (i[0] == '-') { diff--; head.lines.prev--; head.prev.push(i); head.tl.push(transformLeft(i.substr(1))); } else if (i[0] == '+') { diff++; head.lines.now--; head.now.push(i); head.tr.push(transformRight(i.substr(1))); } else { head.lines.prev--; head.prev.push(i); head.lines.now--; head.now.push(i); } if (diff != 0 & i.substr(1, 11) == "Subproject ") { continue; } if (head.lines.prev == 0 && head.lines.now == 0) { if (head.tl.join('\n') != head.tr.join('\n')) { if (!lastDiff || lastDiff != head.diff) { lastDiff = head.diff; res.push(head.diff); res.push(head.index); res.push(head['---']); res.push(head['+++']); } res.push(counts); res.push(head.tempRows.join('\n')); processed.push({ diff:head.diff, index:head.index, "---":head['---'], "+++":head['+++'], cnts: head.counts, rows: head.tempRows }); } head.tl = []; head.tr = []; head.prev = []; head.now = []; head.tempRows = []; counts = null; } } ta[0].value = JSON.stringify(processed,null,2); ta[1].value = res.join('\n'); } function init() { ta = document.getElementsByTagName("TEXTAREA"); } function droppper(e) { e.stopPropagation(); e.preventDefault(); var files = e.dataTransfer.files; // Array of all files for (var i=0, file; file=files[i]; i++) { var reader = new FileReader(); reader.onload = function(e2) { ta[0].value = e2.target.result; process(ta[0].value); } reader.readAsText(file); } } </script>
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

