代码文本对比-前端工具
程序员文章站
2022-07-13 21:51:49
...
代码文本对比
很多时候,我们可能有那种前端代码对比、文本对比的功能
方式一(纯js实现)
- 效果图
- 主要html
如果想要在线编辑对比可使用textare标签,并自定义监听函数去对比渲染
<style>
pre {
width: 50%;
float: left;
}
legend {
margin-bottom: 0;
}
</style>
<body>
<fieldset>
<legend>升级<span style="color: red">前</span><span style="color: blue">后</span>对比效果</legend>
<pre id="pre"></pre>
<pre id="pre2"></pre>
</fieldset>
</body>
- 主要js
// data.appBefore--变更前数据
const bf = data.appBefore.replace(new RegExp("<", "gm"), "<").replace(new RegExp(">", "gm"), ">");
// data.appAfter--变更后数据
// 这里replace是把数据中的<、>替换成<、> 如果想要pre标签中展示html、xml类型代码数据的话
const af = data.appAfter.replace(new RegExp("<", "gm"), "<").replace(new RegExp(">", "gm"), ">");
const result = _eq({value1: bf || bf, value2: af || af});
// 由于这个对比页面是个弹窗,用到了layui,故此方式渲染数据至html标签中
// result.value1 变更前数据
// result.value2 变更后数据
body.find("#pre").html(result.value1);
body.find("#pre2").html(result.value2);
// 核心方法
function _eq(op) {
if(!op){
return op;
}
if(!op.value1_style){
op.value1_style="background-color:#f9e9e9;text-decoration: line-through;color:red";
}
if(!op.value2_style){
op.value2_style="background-color:#ddeeff;";
}
if(!op.eq_min){
op.eq_min=3;
}
if(!op.eq_index){
op.eq_index=5;
}
if (!op.value1 || !op.value2) {
return op;
}
var ps = {
v1_i: 0,
v1_new_value: "",
v2_i: 0,
v2_new_value: ""
};
while (ps.v1_i < op.value1.length && ps.v2_i < op.value2.length) {
if (op.value1[ps.v1_i] === op.value2[ps.v2_i]) {
ps.v1_new_value += op.value1[ps.v1_i].replace(/</g,"<").replace(">",">");
ps.v2_new_value += op.value2[ps.v2_i].replace(/</g,"<").replace(">",">");
ps.v1_i += 1;
ps.v2_i += 1;
if (ps.v1_i >= op.value1.length) {
ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2.substr(ps.v2_i).replace(/</g,"<").replace(">",">") + "</span>";
break;
}
if (ps.v2_i >= op.value2.length) {
ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1.substr(ps.v1_i).replace(/</g,"<").replace(">",">") + "</span>";
break;
}
} else {
ps.v1_index = ps.v1_i + 1;
ps.v1_eq_length = 0;
ps.v1_eq_max = 0;
ps.v1_start = ps.v1_i + 1;
while (ps.v1_index < op.value1.length) {
if (op.value1[ps.v1_index] === op.value2[ps.v2_i + ps.v1_eq_length]) {
ps.v1_eq_length += 1;
} else if (ps.v1_eq_length > 0) {
if (ps.v1_eq_max < ps.v1_eq_length) {
ps.v1_eq_max = ps.v1_eq_length;
ps.v1_start = ps.v1_index - ps.v1_eq_length;
}
ps.v1_eq_length = 0;
break;//只寻找最近的
}
ps.v1_index += 1;
}
if (ps.v1_eq_max < ps.v1_eq_length) {
ps.v1_eq_max = ps.v1_eq_length;
ps.v1_start = ps.v1_index - ps.v1_eq_length;
}
ps.v2_index = ps.v2_i + 1;
ps.v2_eq_length = 0;
ps.v2_eq_max = 0;
ps.v2_start = ps.v2_i + 1;
while (ps.v2_index < op.value2.length) {
if (op.value2[ps.v2_index] === op.value1[ps.v1_i + ps.v2_eq_length]) {
ps.v2_eq_length += 1;
} else if (ps.v2_eq_length > 0) {
if (ps.v2_eq_max < ps.v2_eq_length) {
ps.v2_eq_max = ps.v2_eq_length;
ps.v2_start = ps.v2_index - ps.v2_eq_length;
}
ps.v1_eq_length = 0;
break;//只寻找最近的
}
ps.v2_index += 1;
}
if (ps.v2_eq_max < ps.v2_eq_length) {
ps.v2_eq_max = ps.v2_eq_length;
ps.v2_start = ps.v2_index - ps.v2_eq_length;
}
if (ps.v1_eq_max < op.eq_min && ps.v1_start - ps.v1_i > op.eq_index) {
ps.v1_eq_max = 0;
}
if (ps.v2_eq_max < op.eq_min && ps.v2_start - ps.v2_i > op.eq_index) {
ps.v2_eq_max = 0;
}
if ((ps.v1_eq_max === 0 && ps.v2_eq_max === 0)) {
ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1[ps.v1_i].replace(/</g,"<").replace(">",">") + "</span>";
ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2[ps.v2_i].replace(/</g,"<").replace(">",">") + "</span>";
ps.v1_i += 1;
ps.v2_i += 1;
if (ps.v1_i >= op.value1.length) {
ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2.substr(ps.v2_i).replace(/</g,"<").replace(">",">") + "</span>";
break;
}
if (ps.v2_i >= op.value2.length) {
ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1.substr(ps.v1_i).replace(/</g,"<").replace(">",">") + "</span>";
break;
}
} else if (ps.v1_eq_max > ps.v2_eq_max) {
ps.v1_new_value += "<span style='" + op.value1_style + "'>" + op.value1.substr(ps.v1_i, ps.v1_start - ps.v1_i).replace(/</g,"<").replace(">",">") + "</span>";
ps.v1_i = ps.v1_start;
} else {
ps.v2_new_value += "<span style='" + op.value2_style + "'>" + op.value2.substr(ps.v2_i, ps.v2_start - ps.v2_i).replace(/</g,"<").replace(">",">") + "</span>";
ps.v2_i = ps.v2_start;
}
}
}
op.value1 = ps.v1_new_value;
op.value2 = ps.v2_new_value;
return op;
}
方式二(使用CodeMirror)
效果图
codemirror使用过程
下载项目
github:https://github.com/codemirror/CodeMirror
官网:https://codemirror.net/
打开找到demo html->merge.html
检查是否有codemirror.js
如果没有可以在一个新目录下运行npm install codemirror
把新安装的codemirror工程下的lib的codemirror.js复制过来
打开页面
本地调整后测试的merge.html代码:
<!DOCTYPE html>
<html lang="UTF-8">
<title>CodeMirror: merge view demo</title>
<meta charset="utf-8"/>
<!--<link rel=stylesheet href="../doc/docs.css">-->
<link rel=stylesheet href="../lib/codemirror.css">
<link rel=stylesheet href="../addon/merge/merge.css">
<link rel="stylesheet" href="../theme/darcula.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/css/css.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<script src="../addon/merge/merge.js"></script>
<style>
.CodeMirror {
line-height: 1.2;
}
@media screen and (min-width: 1300px) {
article {
max-width: 1000px;
}
#nav {
border-right: 499px solid transparent;
}
}
span.clicky {
cursor: pointer;
background: #d70;
color: white;
padding: 0 3px;
border-radius: 3px;
}
</style>
<article>
<h2>Compared view</h2>
<div id=view></div>
<script>
var value, orig1, orig2, dv, panes = 2, highlight = true, connect = "align", collapse = false;
function initUI() {
if (value == null) return;
var target = document.getElementById("view");
target.innerHTML = "";
dv = CodeMirror.MergeView(target, {
value: value,
origLeft: panes === 3 ? orig1 : null,
orig: orig2,
lineNumbers: true,
mode: "text/html",
theme: 'darcula',
revertButtons: false,
highlightDifferences: highlight,
connect: connect,
collapseIdentical: collapse
});
}
function toggleDifferences() {
dv.setShowDifferences(highlight = !highlight);
}
window.onload = function () {
value = 'logging:\n' +
' level:\n' +
' root: info\n' +
' org.apache.ibatis: ${LOG_LEVEL:info}\n' +
' io.choerodon: ${LOG_LEVEL:info}\n' +
' org.hzero: ${LOG_LEVEL:info}';
// orig1 = "<!doctype html>\n\n" + value.replace(/\.\.\//g, "codemirror/").replace("yellow", "orange");
orig2 = 'logging:\n' +
' level:\n' +
' root: ${LOG_LEVEL:info}\n' +
' org.apache.ibatis: ${LOG_LEVEL:info}\n' +
' io.choerodon: ${LOG_LEVEL:info}\n' +
' org.hzero: ${LOG_LEVEL:info}\n' +
'\n' +
'# 新加测试配置\n' +
'test:\n' +
' data: test';
initUI();
let d = document.createElement("div");
d.style.cssText = "width: 50px; margin: 7px; height: 14px";
dv.editor().addLineWidget(57, d)
};
function mergeViewHeight(mergeView) {
function editorHeight(editor) {
if (!editor) return 0;
return editor.getScrollInfo().height;
}
return Math.max(editorHeight(mergeView.leftOriginal()),
editorHeight(mergeView.editor()),
editorHeight(mergeView.rightOriginal()));
}
function resize(mergeView) {
var height = mergeViewHeight(mergeView);
for (; ;) {
if (mergeView.leftOriginal())
mergeView.leftOriginal().setSize(null, height);
mergeView.editor().setSize(null, height);
if (mergeView.rightOriginal())
mergeView.rightOriginal().setSize(null, height);
var newHeight = mergeViewHeight(mergeView);
if (newHeight >= height) break;
else height = newHeight;
}
mergeView.wrap.style.height = height + "px";
}
</script>
</article>
</html>
关于merge.html中参数和函数说明自行查看官网https://codemirror.net/demo/merge.html
集成应用
前面是在源项目中测试,html中js和css都是相对源项目的相对路径
集成在自己项目中,需要改造迁移html、js、css,把这些文件迁过来
html换了个名字compares.html,代码:
<!DOCTYPE html>
<html lang="UTF-8">
<head>
<title>详情</title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="codeMirror/codemirror.css">
<link rel="stylesheet" href="codeMirror/merge.css">
<link rel="stylesheet" href="codeMirror/darcula.css">
<link rel="stylesheet" href="codeMirror/base16-light.css">
<style>
.CodeMirror {
line-height: 1.2;
}
@media screen and (min-width: 1300px) {
article {
max-width: 1000px;
}
#nav {
border-right: 499px solid transparent;
}
}
span.clicky {
cursor: pointer;
background: #d70;
color: white;
padding: 0 3px;
border-radius: 3px;
}
h2 {
color: yellowgreen;
}
</style>
</head>
<body>
<article>
<h2>Compared view</h2>
<div id=view></div>
<!-- 引入组件库 -->
<script src="libs/jquery.min.js"></script>
<script src="codeMirror/codemirror.js"></script>
<!-- <script src="src/edit/CodeMirror.js"></script>-->
<script src="codeMirror/diff_match_patch.js"></script>
<script src="codeMirror/merge.js"></script>
<script src="codeMirror/xml.js"></script>
<script src="codeMirror/javascript.js"></script>
<script src="codeMirror/htmlmixed.js"></script>
<script type="text/javascript">
let value, orig1, orig2, dv, panes = 2, highlight = true, connect = null, collapse = false;
let mainUrl, sv, tv;
mainUrl = getQueryString("mainUrl");
sv = getQueryString("sv");
tv = getQueryString("tv");
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return decodeURI(r[2]);
return null;
}
function initUI() {
if (value == null) return;
var target = document.getElementById("view");
target.innerHTML = "";
dv = CodeMirror.MergeView(target, {
value: value,
origLeft: panes === 3 ? orig1 : null,
orig: orig2,
lineNumbers: true,
mode: "text/html",
theme: 'base16-light', // darcula
revertButtons: false,
highlightDifferences: highlight,
connect: connect,
collapseIdentical: collapse
});
}
function toggleDifferences() {
dv.setShowDifferences(highlight = !highlight);
}
window.onload = function () {
$.ajax({
type: "GET",
url: mainUrl + "&sourceVersion=" + sv + "&targetVersion=" + tv,
contentType: "text/plain",
success: function (data) {
value = data.appBefore || data.pomBefore;
orig2 = data.appAfter || data.pomAfter;
initUI();
},
error: function (e) {
alert(e.responseText);
}
});
};
function mergeViewHeight(mergeView) {
function editorHeight(editor) {
if (!editor) return 0;
return editor.getScrollInfo().height;
}
return Math.max(editorHeight(mergeView.leftOriginal()),
editorHeight(mergeView.editor()),
editorHeight(mergeView.rightOriginal()));
}
function resize(mergeView) {
var height = mergeViewHeight(mergeView);
for (; ;) {
if (mergeView.leftOriginal())
mergeView.leftOriginal().setSize(null, height);
mergeView.editor().setSize(null, height);
if (mergeView.rightOriginal())
mergeView.rightOriginal().setSize(null, height);
var newHeight = mergeViewHeight(mergeView);
if (newHeight >= height) break;
else height = newHeight;
}
mergeView.wrap.style.height = height + "px";
}
</script>
</article>
</body>
</html>
这里对比的数据是从后端接口查的,可以一开始写成常量查看效果
<link rel="stylesheet" href="codeMirror/darcula.css">
<link rel="stylesheet" href="codeMirror/base16-light.css">
上面表示的是主题,想要更多的主题去源码工程迁移过来
<script src="codeMirror/diff_match_patch.js"></script>
diff_match_patch.js可以使用demo中的外部路径,我这里是下载下来了
codeMirror中的js和css文件不要落迁移了
其他问题注意自己看控制台检查
推荐阅读
-
前端工程师通过nodejs链接linux,并上传代码进行半自动化更新,省去ssh+ftp的链接工具-前端黑科技-SegmentFault思否
-
原生js基于canvas实现一个简单的前端截图工具代码实例
-
前端代码乱糟糟?是时候引入代码质量检查工具了
-
Python实现的文本对比报告生成工具示例
-
代码文本对比-前端工具
-
自己写代码对比工具
-
整理出来几个比较实用的代码对比工具
-
php-php进阶 - 前端自动化工具如何在已经完成的php框架的代码里起作用?
-
前端工程师通过nodejs链接linux,并上传代码进行半自动化更新,省去ssh+ftp的链接工具-前端黑科技-SegmentFault思否
-
wp文本小工具运行php代码实现面包屑导航步骤