Git Diff 互動式程式異動對照表改良 - 忽略排版空格差異
| | 1 | | ![]() |
三年多前用 diff2html.js 做了一個Git Diff 互動式程式異動表,在工作上使用至今,最近接到顧客意見反應。
使用 Visual Studio / VSCode 維護程式,有時改完程式會順手按一下自動排版、內縮由 Tab 改成空格、或是程式行間增加或刪除換行,這類排版調整原則上不該算是程式異動,但在 diff2html 檢視時都會標示出來。如下圖中身高體重輸入程式段落間的換行、顯示 BMI 時前方移除內縮、SQL 增加內縮空格... 等。
有程式差異比較工具會忽略這類不可見字元 (Whitespace,包含空格、Tab、換行、\r、\v、\f... 等) 異動,例如:VSCode 便會忽略 SELECT 欄位前的空格變化:
我認同排除純 Whitespace 差異有助於降低干擾,聚焦在真正的程式異動上。於是我寫了幾行程式,用 querySelectorAll 篩選出被標示異動的行數,取出異動文字用 Regular Expression 濾掉不可見字元 .replace(/\s/g, '')
,剩餘的可見文字若相同,代表新舊版只差在 Whitespace,便可用 CSS opacity: 0.25 將這類純排版差異調淡,方便眼睛忽略這些差異。
改良版顯示結果如下:
就醬,借用別人分享的成果遇到不同需求,自己改程式再分享,這也是開源的精神之一吧~
程式範例如下:線上展示
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<link rel="stylesheet" type="text/css" href="diff2html.min.css" />
<script src="diff2html.js"></script>
<style>
html,body { height: 100%; margin: 0; padding: 6px; }
.d2h-file-name{ font-size: 9pt; cursor: pointer; }
.d2h-file-list-line { display: block; }
.d2h-file-list-title button { font-size: 9pt; }
</style>
</head>
<body>
<div id=report>
<div id=list></div>
</div>
<script>
function fadeWhiteSpaceDiff(diffHtml) {
diffHtml.querySelectorAll('.d2h-files-diff').forEach(d => {
const tables = d.querySelectorAll('table.d2h-diff-table');
const leftRows = tables[0].querySelectorAll('tr');
const rightRows = tables[1].querySelectorAll('tr');
leftRows.forEach((leftRow, i) => {
if (!leftRow.querySelector('.d2h-del,.d2h-ins')) return;
const rightRow = rightRows[i];
const rightContent = rightRow.querySelector('.d2h-code-line-ctn')?.innerText ?? '';
const leftContent = leftRow.querySelector('.d2h-code-line-ctn')?.innerText ?? '';
if (rightContent.replace(/\s/g, '') === leftContent.replace(/\s/g, '')) {
leftRow.style.opacity = '0.25';
rightRow.style.opacity = '0.25';
}
});
});
}
function showLineByLine(a) {
if (a.showDiff) {
//IE doesn't support element.remove()
var div = a.parentNode.nextElementSibling;
div.parentNode.removeChild(div);
a.showDiff = false;
}
else {
var div = document.createElement("div");
div.innerHTML = Diff2Html.getPrettySideBySideHtmlFromJson(a.diffContent);
a.parentNode.parentNode.appendChild(div);
fadeWhiteSpaceDiff(div);
a.showDiff = true;
}
}
function toggleAll(expand) {
[].forEach.call(document.querySelectorAll('a.d2h-file-name'), function(a) {
if (expand != a.showDiff) showLineByLine(a);
});
}
function doGET(path, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
callback(xhr.responseText);
} else {
callback(null);
}
}
};
xhr.open("GET", path);
xhr.send();
}
doGET("diff.txt", function (diffText) {
var diffFiles = Diff2Html.getJsonFromDiff(diffText);
var diffHtml = Diff2Html.getPrettyHtmlFromJson(diffFiles, {
showFiles: true,
matching: 'lines',
outputFormat: 'side-by-side',
});
//get the file list part
var listHtml = diffHtml.substr(0, diffHtml.indexOf('<div class="d2h-wrapper"'));
document.getElementById('list').innerHTML = listHtml;
[].forEach.call(document.querySelectorAll('a[href]'), function(a, i) {
a.diffContent = [diffFiles[i]];
a.removeAttribute("href");
a.onclick = function() { showLineByLine(a); };
});
var title = document.querySelector('.d2h-file-list-title');
title.innerHTML = title.innerText +
" <button onclick='toggleAll(true)'>Expand All</button>" +
" <button onclick='toggleAll(false)'>Collapse All</button>";
toggleAll(true);
});
</script>
</body>
</html>
The diff2html will show differences including whitespace changes, this article provide a simple way to fade these layout changes in comparison views.
Comments
# by yoyo
自動排版可以設定git hook 在commit前自動執行format tool