三年多前用 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

Post a comment