公司使用 mailman 来显示 patch,mailman 显示的改动只有一堆黑白文字,难以辨认。有时候还把 patch 当成文件再 git add 一遍后再生成一个新的 patch,这时候对于原来 patch 的代码改动来说,整个 patch 都是新增代码,就更加难以看出改动在哪,给 review patch 带来不必要的不便。
有天我想可不可以做成跟 github 一样,至少增删有不同的颜色标记出来。但是又接触不到 mailman 的server,不能改动它的代码。
受启发于 Chrome 插件的 JS 文件注入启发,做了这样一个标签式的插件。只要把链接拖动到浏览器书签栏上生成标签,点击标签就可以生效。
我觉得这样的方式至少有如下几个优点:
1. 免安装,服务器简单,又不需要修改到原网页代码
2. 脚本无缝迭代版本,如果是不兼容的迭代,大不了重新生成个链接让用户拖动就可以了
3. 是否插入脚本过程可控,对于不需要转化的网页,不加载脚本就是了
<a class="bookmark-link" onclick="handleClick(); return false;" href="javascript: void((function() {var element = document.createElement(‘script‘);element.charset = ‘utf-8‘,element.setAttribute(‘src‘, ‘http://127.0.0.1:5120/pretty.js‘);document.body.appendChild(element);})())"><span>Pretty-Patch</span></a>
标签代码
function handleClick() { alert("Drag me to the Bookmarks Bar"); return false; }
如果用户点击链接而不是拖动,就提示他/她
javascript: void ((function () { var element = document.createElement(‘script‘); element.charset = ‘utf-8‘; element.setAttribute(‘src‘, ‘http://127.0.0.1:5120/pretty.js‘); document.body.appendChild(element); } )())
拖动链接到浏览器书签栏上形成是书签的链接执行了以上代码。javascript: void() 就是执行一段不需要返回值的表达式,这里是一个自执行的匿名函数。在这个匿名函数里创建了 script 标签,并将 src 属性设置到脚本所在的位置,这样就在当前网页插入了我们的脚本。
我在 pretty.js 里放入了 jquery 的代码,还有一个最重要的开源库 “diff2html” 的 js 代码,将它的 css 文件内容复制出来当成字符串存到 prettyStyle 变量。
var extralStyle = ‘.d2h-file-wrapper{margin-top:30px; margin-bottom:30px;} body{margin:30px} .copyright{width:100%; text-align:center; margin-top:10px; font-size:0.9em; color:gray;}‘ prettyStyle += extralStyle;
自定义的 css 内容附加到 prettyStyle 变量上
接下来所有自己的 js 代码都放到了一个自执行的匿名函数 (function() { ....... })() 防止污染全局变量。虽然也没啥必要,因为这个注入的 js 文件破坏性地更改了页面,但是是一种习惯吧。
if($("#defeng-is-a-nice-person").length !== 0) { top.document.location.reload(); return; }
这个 id 是注入的 js 文件设置的,这里判断了是否有这个 id,如果有,说明页面以及被格式化了,那么用户再点击一次标签,就重载页面,让用户看到原始页面
var data = $("pre").text(); if(data === undefined) return; data = data.replace(/\\r\\n/g, "\\n").trim(); data = removeFooter(data);
获得 patch 内容
if(data.indexOf("\\n+diff --git") === -1 && data.indexOf("\\[email protected]@ -") === -1) { prettyPatch(data); } else { $("body").empty(); var splitStr = "\\ndiff --git "; var filesArr = []; var headEnd = data.indexOf(splitStr); if(headEnd === -1) return; data = data.slice(headEnd); var each = data.split(splitStr); each.shift(); if(each.length === 0) return; var len = each.length; splitStr = splitStr.slice(1); for(var i=0; i<len; i++) { each[i] = splitStr + each[i] + "\\n"; } prettyPatchPlus(each); }
因为有如下好几种情况:
- patch 是用 diff 生成的
- patch 是用 git format-patch 生成的
- patch 被当作文件又被 git add 后生成新 patch,且还包含 diff 的patch 或者 git format-patch 的 patch
对于前两种情况,直接用 prettyPatch 函数生成结果就可以了
对于内容里包含有 patch 的情况,用 "\\ndiff --git " 作为分隔符将每个文件分割出来后用 prettyPatchPlus 函数生成结果
function removeFooter(str) { return str.trim().replace(/.*?--\\s*\\n\\s*\\d+(.\\d+)+$/, ""); }
这个函数用来移除最后面的 git 版本号
function prettyPatch(str) { var diff2htmlUi = new Diff2HtmlUI({diff: str}); diff2htmlUi.draw(‘body‘, diffOption); }
只要将 diff 文本直接交给 diff2html 库,就可以生成结果用来替换 body 内容
function prettyPatchPlus(each) { var len = each.length; var found = false; for(var i=0; i<len; i++) { var oneFile = each[i]; var lines = oneFile.split("\\n"); var lineLen = lines.length; if(oneFile.indexOf("\\n++++ ") !== -1) { var rmHeader = -1; for(var i=0; i<lineLen; i++) { var line = lines[i]; if(line.slice(0, 3) === "+++" && line.slice(-6) === ".patch") { rmHeader = i + 1; continue; } if(rmHeader === -1) { continue; } if(line.slice(0, 1) === "+") { lines[i] = line.slice(1); } } oneFile = lines.slice(rmHeader).join("\\n"); oneFile = removeFooter(oneFile); } var id = "path-plus-list-" + i; $("<div id=‘" + id +"‘></div>").appendTo("body"); var diff2htmlList = new Diff2HtmlUI({diff: oneFile}); diff2htmlList.draw(‘#‘ + id, diffOption); } }
prettyPatchPlus函数通过判断是否有 "\\n++++ " 来判断一个文件是 diff 文本还是 patch,如果是 patch,将每一行最前面的 + 号移除。
将生成的结果替换到 body 下的 div 标签
$("<style>" + prettyStyle + "</style>").appendTo("head");
附加 diff2html 的 css 样式和自定义样式
$("body").attr("id", "defeng-is-a-nice-person");
标记这页面是经过脚本转化的
var publish = "2017"; var current = (new Date()).getFullYear(); var copyright = "copyright ©2017" + (current == publish? "": "-" + current) + " DNI-XM"; copyright += "\\[email protected]"; $("<hr/><div class=‘copyright‘><span>" + copyright + "</span><div>").appendTo("body");
附加 copyright