前言
提示:这里可以添加本文要记录的大概内容:
提示:以下是本篇文章正文内容,下面案例可供参考
一、monaco-editor
“monaco-editor”: “^0.30.1”
1、编辑器源码
<template>
<div ref="monacoEditor" id="monacoEditor" :style="style"></div>
</template>
<script setup>
import { nextTick, onMounted, ref, watch, onBeforeUnmount } from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
modelValue: {},
style: {},
readOnly: {},
language: {},
content: {},
});
const emit = defineEmits(["update:modelValue"]);
const monacoEditor = ref();
let editor;
const init = () => {
/**
* @param wordWrap 自动换行,注意大小写
*/
editor = monaco.editor.create(monacoEditor.value, {
automaticLayout: true,
value: props.modelValue,
readOnly: props.readOnly,
theme: "vs-dark",
language: props.language,
wordWrap: "on",
wrappingIndent: "indent",
});
// 监听值的变化
editor.onDidChangeModelContent(() => {
emit("update:modelValue", editor.getValue());
});
};
onMounted(() => {
init();
});
watch(
() => props.language,
(nv, ov) => {
monaco.editor.setModelLanguage(editor.getModel(), nv);
}
);
function updateValue() {
setTimeout(() => {
editor.setValue(props.modelValue);
}, 200);
}
watch(
() => props.language,
(newValue) => {
monaco.editor.setModelLanguage(editor.getModel(), newValue);
}
);
defineExpose({ updateValue });
</script>
<style></style>
<config-edit
v-model="tempFlow.textareashow"
:language="tempFlow.language"
:readOnly="false"
style="height: 100%; width: 100%"
></config-edit>
2、对比diff
ps:在比较过程中,支持在右侧的进行文件编辑。可通过界面顶部操作栏,查看当前修改个数(实时更改)。
当前修改处,可通过点击下一个,上一个跳转。当用户选择某行,当前修改处也会发生变化,
需要注意的是,如果当前选中行,不在修改的范围内,则显示0,点击跳转按钮,将跳转到离当前最近的一个修改,并且标识当前处于第几个修改
<template>
<div class="monaco-diff-box">
<div class="layout-start operate">
<div style="width: 50%">{{ leftText }}</div>
<div style="width: 10%">{{ rightText }}</div>
<div class="layout-end" style="width: 40%">
<span
>发现 <span class="config-diff-count">{{ diffCount }}</span
> 处修改</span
>
<span
>,当前第 <span class="config-diff-count">{{
curDiffCount || 0
}}</span
> 处 </span
>
<span
title="上一个"
v-debounce-click="
() => {
exeCommand('goPrevDiff');
}
"
class="diffJump"
>
<el-icon><CaretLeft /></el-icon>
上一个
</span>
<span
title="下一个"
v-debounce-click="
() => {
exeCommand('goNextDiff');
}
"
class="diffJump"
>
下一个
<el-icon><CaretRight /></el-icon>
</span>
</div>
</div>
<div ref="monacoDiff" id="monacoDiff" :style="style"></div>
</div>
</template>
<script setup>
import {
nextTick,
onMounted,
ref,
watch,
onBeforeUnmount,
reactive,
computed,
} from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
newCode: {},
oldCode: {},
params: {},
language: {},
});
let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");
const monacoDiff = ref();
const lineChangesDataList = ref([]);
// 当前一共有多少处修改
let diffCount = computed(() => {
return lineChangesDataList.value.length;
});
// 当前cursor指示的几处修改
let curDiffCount = ref(1);
var editor = null;
var decorations = null;
var navi;
var lineChangesDataMap = new Map();
const init = () => {
editor = monaco.editor.createDiffEditor(monacoDiff.value, {
automaticLayout: true,
readOnly: false,
wordWrap: "on",
theme: "vs",
wrappingIndent: "indent",
bracketPairColorization: true,
diffCodeLens: true,
experimental: {
collapseUnchangedRegions: true, // 折叠未更改区域
},
enableSplitViewResizing: false, // 允许用户调整差异编辑器拆分视图的大小。默认为 true。
});
let originalModel = monaco.editor.createModel(props.oldCode, props.language);
let modifiedModel = monaco.editor.createModel(props.newCode, props.language);
editor.setModel({
original: originalModel,
modified: modifiedModel,
});
// decorations = editor.createDecorationsCollection([
// {
// range: new monaco.Range(3, 1, 3, 1),
// options: {
// isWholeLine: true,
// className: "myContentClass",
// glyphMarginClassName: "myGlyphMarginClass",
// },
// },
// ]);
navi = monaco.editor.createDiffNavigator(editor, {
followsCaret: true,
ignoreCharChanges: true,
});
};
onMounted(() => {
init();
setTimeout(() => {
editor.onDidUpdateDiff((val) => {
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(
editor.getPosition().lineNumber
);
});
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
editor.getModifiedEditor().onMouseDown((e) => {
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
});
editor.getOriginalEditor().onMouseDown((e) => {
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
});
}, 200);
});
/**
* @desc 跳转修改处
* @cmd string
* goNextDiff 前进异步
* goPrevDiff 后退一步
*/
const exeCommand = (cmd) => {
let arr = Array.from(lineChangesDataMap.keys());
let positionIndex;
let lineNumber = editor.getPosition().lineNumber;
let hasNewNum = false;
if (arr.indexOf(lineNumber) == -1) {
hasNewNum = true;
arr.push(lineNumber);
arr = arr.sort((a, b) => {
return a - b;
});
}
positionIndex = arr.indexOf(lineNumber);
if (cmd === "goNextDiff") {
setTimeout(() => {
//下一步
if (
curDiffCount.value == lineChangesDataList.value.length ||
(hasNewNum && arr[arr.length - 1] == lineNumber)
) {
curDiffCount.value = 1;
} else {
curDiffCount.value = lineChangesDataMap.get(arr[positionIndex + 1]);
}
navi.next();
}, 300);
} else if (cmd === "goPrevDiff") {
setTimeout(() => {
//上一步
if (curDiffCount.value == 1 || (hasNewNum && arr[0] == lineNumber)) {
curDiffCount.value = lineChangesDataList.value.length;
} else {
curDiffCount.value = lineChangesDataMap.get(arr[positionIndex - 1]);
}
nextTick(() => {
navi.previous();
});
});
}
};
/**
* @desc 获取最新编辑数据
*/
const getValue = () => {
return editor.getModel().modified.getValue();
}
watch(
() => props.language,
(nv, ov) => {
// monaco.editor.setModelLanguage(editor.getModel(), nv);
}
);
defineExpose({
getValue,
});
</script>
<style lang="less">
.monaco-diff-box {
height: 100%;
.operate {
height: 36px;
.config-diff-count {
color: #ff5722;
}
.diffJump {
cursor: pointer;
}
}
#monacoDiff {
flex: 1;
height: calc(100% - 36px);
.monaco-editor .line-numbers.active-line-number {
color: #ff5722;
}
}
}
</style>
2023/12/05
支持相同代码可折叠
这个需要升级monaco的版本
“monaco-editor”: “^0.44.0”
由于这个需要和webpack5才能兼容,所以不是webpack5的需要升级(vue-cli5)
升级manaco之后createDiffNavigator,被删除了,在这个地方化解决了createDiffNavigator被删除之后的,修改处跳转的问题
<template>
<div class="monaco-diff-box">
<div class="layout-start operate">
<div style="width: 50%">{{ leftText }}</div>
<div style="width: 10%">{{ rightText }}</div>
<div class="layout-end" style="width: 40%">
<span
>发现 <span class="config-diff-count">{{ diffCount }}</span
> 处修改</span
>
<span
>,当前第 <span class="config-diff-count">{{
curDiffCount || 0
}}</span
> 处 </span
>
<span
title="上一个"
v-debounce-click="
() => {
exeCommand('goPrevDiff');
}
"
class="diffJump"
>
<el-icon><CaretLeft /></el-icon>
上一个
</span>
<span
title="下一个"
v-debounce-click="
() => {
exeCommand('goNextDiff');
}
"
class="diffJump"
>
下一个
<el-icon><CaretRight /></el-icon>
</span>
</div>
</div>
<div ref="monacoDiff" id="monacoDiff" :style="style"></div>
</div>
</template>
<script setup>
import {
nextTick,
onMounted,
ref,
watch,
onBeforeUnmount,
reactive,
computed,
} from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
newCode: {},
oldCode: {},
params: {},
language: {},
});
let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");
const monacoDiff = ref();
const lineChangesDataList = ref([]);
// 当前一共有多少处修改
let diffCount = computed(() => {
return lineChangesDataList.value.length;
});
// 当前cursor指示的几处修改
let curDiffCount = ref(1);
var editor = null;
var lineChangesDataMap = new Map();
let monaco_languages = monaco.languages.getLanguages();
const init = () => {
editor = monaco.editor.createDiffEditor(monacoDiff.value, {
automaticLayout: true,
readOnly: false,
wordWrap: "on",
theme: "vs",
wrappingIndent: "indent",
bracketPairColorization: true,
diffCodeLens: true,
ignoreTrimWhitespace: false, //是否忽略差异编辑器中的空格
enableSplitViewResizing: false, // 允许用户调整差异编辑器拆分视图的大小。默认为 true。
hideUnchangedRegions: {
enabled: true,
},
});
let id = "plaintext"; // 默认纯文本
monaco_languages.forEach((language) => {
language.extensions &&
language.extensions.forEach((ex) => {
if (props.language == ex.slice(1)) {
id = language.id;
}
});
});
let originalModel = monaco.editor.createModel(props.oldCode, id);
let modifiedModel = monaco.editor.createModel(props.newCode, id);
editor.setModel({
original: originalModel,
modified: modifiedModel,
});
};
onMounted(() => {
init();
setTimeout(() => {
editor.onDidUpdateDiff((val) => {
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(
editor.getPosition().lineNumber
);
});
lineChangesDataList.value = editor.getLineChanges();
editor.setPosition({
column: 1,
lineNumber: lineChangesDataList.value[0].modifiedEndLineNumber,
});
editor
.getModifiedEditor()
.revealLineInCenter(
lineChangesDataList.value[0].modifiedStartLineNumber,
lineChangesDataList.value[0].modifiedEndLineNumber
);
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
editor.getModifiedEditor().onMouseDown((e) => {
if (e?.target?.position?.lineNumber) {
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(
e.target.position.lineNumber
);
} else {
curDiffCount.value = 0;
}
});
editor.getOriginalEditor().onMouseDown((e) => {
if (e?.target?.position?.lineNumber) {
lineChangesDataList.value = editor.getLineChanges();
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(
e.target.position.lineNumber
);
} else {
curDiffCount.value = 0;
}
});
}, 500);
});
/**
* @desc 跳转修改处
* @cmd string
* goNextDiff 前进异步
* goPrevDiff 后退一步
*/
const exeCommand = (cmd) => {
let arr = Array.from(lineChangesDataMap.keys());
let positionIndex;
let lineNumber = editor.getPosition().lineNumber;
let hasNewNum = false;
if (arr.indexOf(lineNumber) == -1) {
hasNewNum = true;
arr.push(lineNumber);
arr = arr.sort((a, b) => {
return a - b;
});
}
positionIndex = arr.indexOf(lineNumber);
if (cmd === "goNextDiff") {
setTimeout(() => {
//下一步
if (
curDiffCount.value == lineChangesDataList.value.length ||
(hasNewNum && arr[arr.length - 1] == lineNumber)
) {
curDiffCount.value = 1;
editor
.getModifiedEditor()
.revealLineInCenter(
lineChangesDataList.value[0].modifiedStartLineNumber,
lineChangesDataList.value[0].modifiedEndLineNumber
);
editor.setPosition({
column: 1,
lineNumber: lineChangesDataList.value[0].modifiedEndLineNumber,
});
} else {
curDiffCount.value = lineChangesDataMap.get(arr[positionIndex + 1]);
editor
.getModifiedEditor()
.revealLineInCenter(arr[positionIndex + 1], arr[positionIndex + 1]);
editor.setPosition({
column: 1,
lineNumber: arr[positionIndex + 1],
});
}
}, 300);
} else if (cmd === "goPrevDiff") {
setTimeout(() => {
//上一步
if (curDiffCount.value == 1 || (hasNewNum && arr[0] == lineNumber)) {
let diffNum = lineChangesDataList.value.length;
curDiffCount.value = diffNum;
editor
.getModifiedEditor()
.revealLineInCenter(
lineChangesDataList.value[diffNum - 1].modifiedStartLineNumber,
lineChangesDataList.value[diffNum - 1].modifiedEndLineNumber
);
editor.setPosition({
column: 1,
lineNumber:
lineChangesDataList.value[diffNum - 1].modifiedEndLineNumber,
});
} else {
curDiffCount.value = lineChangesDataMap.get(arr[positionIndex - 1]);
editor
.getModifiedEditor()
.revealLineInCenter(arr[positionIndex - 1], arr[positionIndex - 1]);
editor.setPosition({
column: 1,
lineNumber: arr[positionIndex - 1],
});
}
});
}
};
/**
* @desc 获取最新编辑数据
*/
const getValue = () => {
return editor.getModel().modified.getValue();
};
watch(
() => props.language,
(nv, ov) => {
// monaco.editor.setModelLanguage(editor.getModel(), nv);
}
);
defineExpose({
getValue,
});
</script>
<style lang="less">
.monaco-diff-box {
height: 100%;
.operate {
height: 36px;
.config-diff-count {
color: #ff5722;
}
.diffJump {
cursor: pointer;
}
}
#monacoDiff {
flex: 1;
height: calc(100% - 36px);
.monaco-editor .line-numbers.active-line-number {
color: #ff5722;
}
}
}
</style>
2024/04/12
修复格式校验,修改格式为正常之后,还是又报错标记返回
<template>
<div class="monaco-diff-box">
<div class="layout-start operate">
<div style="width: 50%">{{ leftText }}</div>
<div style="width: 10%">{{ rightText }}</div>
<div class="layout-end" style="width: 40%">
<span
>发现 <span class="config-diff-count">{{ diffCount }}</span
> 处修改</span
>
<span
>,当前第 <span class="config-diff-count">{{
curDiffCount || 0
}}</span
> 处 </span
>
<span
title="上一个"
v-debounce-click="
() => {
exeCommand('goPrevDiff');
}
"
class="diffJump"
>
<el-icon><CaretLeft /></el-icon>
上一个
</span>
<span
title="下一个"
v-debounce-click="
() => {
exeCommand('goNextDiff');
}
"
class="diffJump"
>
下一个
<el-icon><CaretRight /></el-icon>
</span>
</div>
</div>
<div ref="monacoDiff" id="monacoDiff" :style="style"></div>
</div>
</template>
<script setup>
import {
nextTick,
onMounted,
ref,
watch,
onBeforeUnmount,
reactive,
computed,
onUnmounted,
} from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
newCode: {},
oldCode: {},
params: {},
language: {},
});
let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");
let originalModel = "";
let modifiedModel = "";
const monacoDiff = ref();
const lineChangesDataList = ref([]);
// 当前一共有多少处修改
let diffCount = computed(() => {
return lineChangesDataList.value?.length || 0;
});
// 当前cursor指示的几处修改
let curDiffCount = ref(0);
var editor = null;
var lineChangesDataMap = new Map();
let monaco_languages = monaco.languages.getLanguages();
let id = "plaintext"; // 默认纯文本
const init = async () => {
editor = await monaco.editor.createDiffEditor(monacoDiff.value, {
automaticLayout: true,
readOnly: false,
wordWrap: "on",
theme: "vs",
wrappingIndent: "indent",
bracketPairColorization: true,
diffCodeLens: true,
ignoreTrimWhitespace: false, //是否忽略差异编辑器中的空格
enableSplitViewResizing: false, // 允许用户调整差异编辑器拆分视图的大小。默认为 true。
hideUnchangedRegions: {
enabled: true,
},
diffWordWrap: "off",
});
monaco_languages.forEach((language) => {
language.extensions &&
language.extensions.forEach((ex) => {
if (props.language == ex.slice(1)) {
id = language.id;
}
});
});
// Set up the XML language
monaco.languages.register({ id: "xml" });
monaco.languages.setLanguageConfiguration("xml", {
comments: {
lineComment: "--",
blockComment: ["--[[", "]]"],
},
});
// Define the error checking function for XML
const checkXMLSyntax = (code) => {
let errorMessage, xmlDoc;
// code for IE
if (window.ActiveXObject) {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlContent);
if (xmlDoc.parseError.errorCode != 0) {
errorMessage = "错误code: " + xmlDoc.parseError.errorCode + "\n";
errorMessage = errorMessage + "错误原因: " + xmlDoc.parseError.reason;
errorMessage = errorMessage + "错误位置: " + xmlDoc.parseError.line;
return [
{
severity: monaco.MarkerSeverity.Error,
message: errorMessage,
startLineNumber: xmlDoc.parseError.line - 1,
startColumn: 0,
endLineNumber: xmlDoc.parseError.line - 1,
endColumn: 1,
},
];
} else {
return [];
}
} else if (document.implementation.createDocument) {
const parser = new DOMParser();
xmlDoc = parser.parseFromString(code, "text/xml");
const error = xmlDoc.getElementsByTagName("parsererror");
if (error.length > 0) {
errorMessage = xmlDoc
.getElementsByTagName("parsererror")[0]
.getElementsByTagName("div")[0].innerText;
let tmp = [];
let keyRexExp = new RegExp("(line 0)", "gi");
errorMessage.split(":").forEach(function (msg, index) {
if (index == errorMessage.split(":").length - 1) {
msg = msg.replace(keyRexExp, "");
tmp.push(msg);
} else {
tmp.push(msg);
}
});
errorMessage = tmp.join(":");
let match = errorMessage.match(/line (\d+) at column (\d+)/);
if (match) {
let lineNumber = +match[1];
let columnNumber = +match[2];
return [
{
severity: monaco.MarkerSeverity.Error,
message: errorMessage,
startLineNumber: lineNumber,
startColumn: columnNumber,
endLineNumber: lineNumber,
endColumn: columnNumber,
},
];
} else {
return [];
}
} else {
return [];
}
}
};
// Set up the YAML language
monaco.languages.register({ id: "yaml" });
monaco.languages.setLanguageConfiguration("yaml", {
comments: {
lineComment: "--",
blockComment: ["--[[", "]]"],
},
});
// Define the error checking function for YAML
const checkYAMLSyntax = (code) => {
try {
yaml.load(code);
if (yaml == null) {
}
return [];
} catch (error) {
return [
{
severity: monaco.MarkerSeverity.Error,
message: error.message,
startLineNumber: error.mark.line,
startColumn: error.mark.column,
endLineNumber: error.mark.line,
endColumn: error.mark.column + 1,
},
];
}
};
// Set up the Lua language
monaco.languages.register({ id: "lua" });
monaco.languages.setLanguageConfiguration("lua", {
comments: {
lineComment: "--",
blockComment: ["--[[", "]]"],
},
});
// Define the error checking function
function checkLuaSyntax(code) {
try {
luaparse.parse(code);
return [];
} catch (error) {
return [
{
severity: monaco.MarkerSeverity.Error,
message: error.message,
startLineNumber: error.line,
startColumn: error.column + 1,
endLineNumber: error.line,
endColumn: error.column + 2,
},
];
}
}
originalModel = await monaco.editor.createModel(props.oldCode, id);
modifiedModel = await monaco.editor.createModel(props.newCode, id);
editor.setModel({
original: originalModel,
modified: modifiedModel,
});
let markers = [];
if (id === "xml") {
markers = checkXMLSyntax(modifiedModel.getValue());
} else if (id === "yaml") {
markers = checkYAMLSyntax(modifiedModel.getValue());
} else if (id === "lua") {
markers = checkLuaSyntax(modifiedModel.getValue());
}
monaco.editor.setModelMarkers(modifiedModel, id, markers);
modifiedModel.onDidChangeContent(() => {
let markers = [];
if (id === "xml") {
markers = checkXMLSyntax(modifiedModel.getValue());
} else if (id === "yaml") {
markers = checkYAMLSyntax(modifiedModel.getValue());
} else if (id === "lua") {
markers = checkLuaSyntax(modifiedModel.getValue());
}
monaco.editor.setModelMarkers(modifiedModel, id, markers);
});
editor.onDidUpdateDiff((val) => {
lineChangesDataList.value = editor.getLineChanges();
if (lineChangesDataList.value?.length > 0) {
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
curDiffCount.value = lineChangesDataMap.get(
editor.getPosition().lineNumber
);
}
});
lineChangesDataList.value = editor.getLineChanges();
if (lineChangesDataList.value?.length > 0) {
curDiffCount.value = 1;
editor.setPosition({
column: 1,
lineNumber: lineChangesDataList.value[0].modifiedEndLineNumber,
});
editor
.getModifiedEditor()
.revealLineInCenter(
lineChangesDataList.value[0].modifiedStartLineNumber,
lineChangesDataList.value[0].modifiedEndLineNumber
);
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
}
editor.getModifiedEditor().onMouseDown((e) => {
if (e?.target?.position?.lineNumber) {
lineChangesDataList.value = editor.getLineChanges();
if (lineChangesDataList.value?.length > 0) {
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
}
curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
} else {
curDiffCount.value = 0;
}
});
editor.getOriginalEditor().onMouseDown((e) => {
if (e?.target?.position?.lineNumber) {
lineChangesDataList.value = editor.getLineChanges();
if (lineChangesDataList.value?.length > 0) {
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
}
curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
} else {
curDiffCount.value = 0;
}
});
};
onMounted(() => {
init();
});
/**
* @desc 跳转修改处
* @cmd string
* goNextDiff 前进异步
* goPrevDiff 后退一步
*/
const exeCommand = (cmd) => {
if (lineChangesDataList.value.length === 0) return;
let arr = Array.from(lineChangesDataMap.keys());
let positionIndex;
let lineNumber = editor.getPosition().lineNumber;
let hasNewNum = false;
if (arr.indexOf(lineNumber) == -1) {
hasNewNum = true;
arr.push(lineNumber);
arr = arr.sort((a, b) => {
return a - b;
});
}
positionIndex = arr.indexOf(lineNumber);
if (cmd === "goNextDiff") {
setTimeout(() => {
//下一步
if (
curDiffCount.value == lineChangesDataList.value?.length ||
(hasNewNum && arr[arr.length - 1] == lineNumber)
) {
curDiffCount.value = 1;
editor
.getModifiedEditor()
.revealLineInCenter(
lineChangesDataList.value[0].modifiedStartLineNumber,
lineChangesDataList.value[0].modifiedEndLineNumber
);
editor.setPosition({
column: 1,
lineNumber: lineChangesDataList.value[0]?.modifiedEndLineNumber,
});
} else {
curDiffCount.value = lineChangesDataMap.get(arr[positionIndex + 1]);
editor
.getModifiedEditor()
.revealLineInCenter(arr[positionIndex + 1], arr[positionIndex + 1]);
editor.setPosition({
column: 1,
lineNumber: arr[positionIndex + 1],
});
}
}, 300);
} else if (cmd === "goPrevDiff") {
setTimeout(() => {
//上一步
if (curDiffCount.value == 1 || (hasNewNum && arr[0] == lineNumber)) {
let diffNum = lineChangesDataList.value.length;
curDiffCount.value = diffNum;
editor
.getModifiedEditor()
.revealLineInCenter(
lineChangesDataList.value[diffNum - 1]?.modifiedStartLineNumber,
lineChangesDataList.value[diffNum - 1]?.modifiedEndLineNumber
);
editor.setPosition({
column: 1,
lineNumber:
lineChangesDataList.value[diffNum - 1]?.modifiedEndLineNumber,
});
} else {
curDiffCount.value = lineChangesDataMap.get(arr[positionIndex - 1]);
editor
.getModifiedEditor()
.revealLineInCenter(arr[positionIndex - 1], arr[positionIndex - 1]);
editor.setPosition({
column: 1,
lineNumber: arr[positionIndex - 1],
});
}
});
}
};
/**
* @desc 获取最新编辑数据
*/
const getValue = () => {
return editor.getModel().modified.getValue();
};
/**
* @desc 判断当前的languageId
*/
function getLanguageId(curLanguage) {
return id;
}
/**
* @desc 设置编辑器的位置
*/
function setPosition(column, lineNumber) {
editor.setPosition({
column: column,
lineNumber: lineNumber,
});
editor.getModifiedEditor().revealLineInCenter(lineNumber);
lineChangesDataList.value = editor.getLineChanges();
if (lineChangesDataList.value?.length > 0) {
lineChangesDataList.value.forEach((item, index) => {
if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
} else {
for (
let i = item.modifiedStartLineNumber;
i <= item.modifiedEndLineNumber;
i++
) {
lineChangesDataMap.set(i, index + 1);
}
}
});
}
curDiffCount.value = lineChangesDataMap.get(lineNumber);
}
/**
* @desc 获取标记的信息
*/
function getModelMarkers() {
// setTimeout(() => {
// console.log("editor.getModel().modified.", editor.getModel().modified.getModelMarkers())
console.log("modifiedModel.uri", modifiedModel.uri);
console.log(
"monaco.editor.getModelMarkers();",
monaco.editor.getModelMarkers(modifiedModel.uri)
);
console.log(
"1111111111111",
monaco.editor.getModelMarkers({
resource: modifiedModel.uri,
})
);
return monaco.editor.getModelMarkers({
resource: modifiedModel.uri,
});
// })
}
/**
* @desc monaco注销
*/
function dispose() {
editor.dispose();
}
defineExpose({
getValue,
getLanguageId,
setPosition,
getModelMarkers,
dispose,
});
/**
* @desc 清理缓存
*/
onUnmounted(() => {});
</script>
<style lang="less">
.monaco-diff-box {
height: 100%;
.operate {
height: 36px;
.config-diff-count {
color: #ff5722;
}
.diffJump {
cursor: pointer;
}
}
#monacoDiff {
flex: 1;
height: calc(100% - 36px);
.monaco-editor .line-numbers.active-line-number {
color: #ff5722;
}
}
}
</style>
2、体积优化
待续…
二、ace-editor?
1、源码
代码如下(示例):
<template>
<v-ace-editor
v-model:value="modelValue"
@init="init"
lang="json"
:theme="theme"
:options="options"
:readonly="readonly"
:style="style"
/>
</template>
<script setup>
import { VAceEditor } from "vue3-ace-editor";
import "ace-builds/webpack-resolver";
import "ace-builds/src-noconflict/mode-json";
import "ace-builds/src-noconflict/theme-chrome";
import "ace-builds/src-noconflict/ext-language_tools";
const props = defineProps({
modelValue: {},
theme: {},
options: {},
readonly: {},
//自定义行内样式
style: {},
})
</script>
<style>
</style>
<ace-editor
v-model:value="tempFlow.textareashow"
@init="initFail"
lang="json"
:theme="aceConfig.theme"
:options="aceConfig.options"
:readonly="aceConfig.readOnly"
class="ace-editor"
/>
2、体积优化
待续…
3、codemirror
1、diff
<template>
<div class="config-diff">
<div class="config-diff-title">
<div class="layout-start">
<div style="width: 50%">{{ leftText }}</div>
<div style="width: 10%">{{ rightText }}</div>
<div class="layout-end" style="width: 40%">
<span
>发现 <span class="config-diff-count">{{ diffCount }}</span
> 处修改</span
>
<span
>,当前第 <span class="config-diff-count">{{
curDiffCount
}}</span
> 处</span
>
<span
title="上一个"
v-debounce-click="
() => {
exeCommand('goPrevDiff');
}
"
class="diffJump"
>
<el-icon><CaretLeft /></el-icon>
上一个
</span>
<span
title="下一个"
v-debounce-click="
() => {
exeCommand('goNextDiff');
}
"
class="diffJump"
>
下一个
<el-icon><CaretRight /></el-icon>
</span>
</div>
</div>
</div>
<div class="merge-view-main">
<div class="file-editor" id="config-diff-view" ref="configDiffView"></div>
</div>
</div>
</template>
<script setup>
import { ref, getCurrentInstance, onMounted, watch } from "vue";
import _ from "lodash";
// 引入全局实例
import CodeMirror from "codemirror";
// 核心样式
import "codemirror/lib/codemirror.css";
// 引入主题后还需要在 options 中指定主题才会生效
import "codemirror/theme/idea.css";
// 需要引入具体的语法高亮库才会有对应的语法高亮效果
// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/css/css.js";
import "codemirror/mode/xml/xml.js";
import "codemirror/mode/shell/shell.js";
import "codemirror/mode/sql/sql.js";
//代码补全提示
import "codemirror/addon/hint/anyword-hint.js";
import "codemirror/addon/hint/css-hint.js";
import "codemirror/addon/hint/html-hint.js";
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/sql-hint.js";
import "codemirror/addon/hint/xml-hint.js";
//代码版本差异比较
import "codemirror/addon/merge/merge.js";
import "codemirror/addon/merge/merge.css";
import DiffMatchPatch from "diff-match-patch";
window.diff_match_patch = DiffMatchPatch;
window.DIFF_DELETE = -1;
window.DIFF_INSERT = 1;
window.DIFF_EQUAL = 0;
const props = defineProps({
newCode: {},
oldCode: {},
params: {},
});
const { proxy } = getCurrentInstance();
//文件后缀和codemirror模式
const FILE_POSTFIX = {
xml: "application/xml",
json: "application/json",
txt: "text/html",
lua: "text/x-lua",
py: "text/x-python",
js: "text/javascript",
sh: "text/x-sh",
other: "text/x-textile",
};
let configDiffView = ref(null);
var dv;
// 当前一共有多少处修改
let diffCount = ref(0);
// 当前cursor指示的几处修改
let curDiffCount = ref(0);
let newCode = "";
let oldCode = "";
let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");
const postfix = proxy.$commonSvc.getFilePostfix(props.params.name);
const mode = FILE_POSTFIX[postfix] || FILE_POSTFIX.other;
function initUI(target) {
if (oldCode == null) return;
target.innerHTML = "";
dv = CodeMirror.MergeView(target, {
value: newCode,
origLeft: oldCode,
orig: null,
lineNumbers: true, //显示行号
lineWrapping: true,
readOnly: false,
mode: mode,
autofocus: true,
foldGutter: true, //代码折叠
highlightDifferences: "highlight", //有差异的地方是否高亮
collapseIdentical: true,
connect: null,
});
setTimeout(function () {
dv.editor().refresh();
dv.leftOriginal() && dv.leftOriginal().refresh();
}, 500);
var diffs = dv.leftChunks();
diffCount.value = (diffs && diffs.length) || 0;
curDiffCount.value = 0;
}
/**
* @desc exeCommand 执行codemirror mergeview的命令类型
* @param {*} cmd
* 前进一步(goNextDiff) or
* 后退异步(goPrevDiff)
*/
const exeCommand = (cmd) => {
if (diffCount.value == 0) {
return;
} else {
return new Promise((resolve, reject) => {
if (dv) {
if (cmd === "goNextDiff") {
//下一步
if (curDiffCount.value < diffCount.value) {
++curDiffCount.value;
} else {
curDiffCount.value = 1;
}
} else if (cmd === "goPrevDiff") {
//上一步
if (curDiffCount.value > 1) {
curDiffCount.value--;
} else {
curDiffCount.value = diffCount.value;
}
}
if (curDiffCount.value === 0) return;
// 延时,等待diffView刷新后,在定位,存在一定的误差,主要在于不确定diffView刷新需要的时间
setTimeout(function () {
proxy.$commonSvc.highlightCodeMirror(
configDiffView.value,
curDiffCount.value
);
}, 300);
}
});
}
};
/**
* @desc getMergeConfig
*/
const getMergeConfig = () => {
if (!dv) {
return;
}
var edit = dv.edit;
if (!edit) {
return;
}
return edit.getValue();
};
onMounted(() => {
newCode = props.newCode;
oldCode = props.oldCode;
var target = configDiffView.value;
initUI(target);
});
watch(
() => [props.newCode, props.params],
(newValue) => {
newCode = props.newCode;
oldCode = props.oldCode;
var target = configDiffView.value;
initUI(target);
},
{
deep: true,
}
);
defineExpose({
getMergeConfig,
});
</script>
<style lang="less" scoped>
.config-diff {
height: 100%;
display: flex;
flex-direction: column;
padding: 0 16px;
.config-diff-title {
height: 36px;
.config-diff-count {
color: #ff5722;
}
}
.merge-view-main {
flex: 1;
.file-editor {
height: 600px;
.CodeMirror-merge,
.CodeMirror {
z-index: 88888;
height: 520px !important;
}
}
.light-cursor {
background: #b390f7;
}
}
@media (min-width: 760px) {
.merge-view-main {
.file-editor {
.CodeMirror-merge,
.CodeMirror {
height: 290px !important;
}
}
}
}
@media (min-width: 1440px) {
.merge-view-main {
.file-editor {
.CodeMirror-merge,
.CodeMirror {
height: 520px !important;
}
}
}
}
.dev-test {
.cm-s-blackboard.CodeMirror,
.CodeMirror-merge-copybuttons-right,
svg,
.CodeMirror-merge-2pane .CodeMirror-merge-gap {
height: 100%;
}
}
.merge-event {
.CodeMirror-merge-copy {
pointer-events: auto !important;
}
}
}
.diffJump {
cursor: pointer;
}
</style>
/**
* @desc codemirror merge next和pre 高亮
* @param codeWrap {{string|Dom Element}} 需要检查的code所属的一个dom元素,目的缩小查询范围
* @param lineNumber {{number}} 需要高亮的行
*/
highlightCodeMirror(codeWrap, curDiffCount) {
if (!codeWrap) {
return false;
}
var lines_left = $(".CodeMirror-merge-left", $(codeWrap))
.find(".CodeMirror-linebackground")
.get();
var lines_right = $(".CodeMirror-merge-editor", $(codeWrap))
.find(".CodeMirror-linebackground")
.get();
var merge_left = $(".CodeMirror-merge-left", $(codeWrap))
.find(".CodeMirror-merge-l-chunk")
.get();
var merge_right = $(".CodeMirror-merge-editor", $(codeWrap))
.find(".CodeMirror-merge-l-chunk")
.get();
var lineNumber_left;
var lineNumber_right;
var merge_left_end = $(".CodeMirror-merge-left", $(codeWrap)).find(
".CodeMirror-merge-l-chunk-end"
);
var merge_right_end = $(".CodeMirror-merge-editor", $(codeWrap)).find(
".CodeMirror-merge-l-chunk-end"
);
var lineNumber_left_end = merge_left_end
.eq(curDiffCount - 1)
.parent()
.find(".CodeMirror-linenumber")
.text();
var lineNumber_right_end = merge_right_end
.eq(curDiffCount - 1)
.parent()
.find(".CodeMirror-linenumber")
.text();
/**
* @desc 定位每次修改高亮的起始和结束位置
*/
for (var i = lines_left.length - 1; i >= 0; i--) {
var temp = $(lines_left[i]).parent().find(".CodeMirror-linenumber");
var value = parseInt($(temp).text());
var value_end = parseInt(lineNumber_left_end);
if (value <= value_end) {
if (
value < value_end &&
$(lines_left[i]).attr("class").indexOf("-end") != -1
) {
break;
}
if ($(lines_left[i]).attr("class").indexOf("-start") != -1) {
lineNumber_left = $(temp).text();
}
}
}
for (var i = lines_right.length - 1; i >= 0; i--) {
var temp = $(lines_right[i]).parent().find(".CodeMirror-linenumber");
var value = parseInt($(temp).text());
var value_end = parseInt(lineNumber_right_end);
if (value <= value_end) {
if (
value < value_end &&
$(lines_right[i]).attr("class").indexOf("-end") != -1
) {
break;
}
if ($(lines_right[i]).attr("class").indexOf("-start") != -1) {
lineNumber_right = $(temp).text();
}
}
}
/**
* @desc 注入样式
*/
merge_left.forEach(function (item) {
var temp = $(item).parent().find(".CodeMirror-linenumber");
if (
lineNumber_left &&
parseInt($(temp).text()) >= parseInt(lineNumber_left) &&
parseInt($(temp).text()) <= parseInt(lineNumber_left_end)
) {
$(item).addClass("light-cursor");
} else {
$(item).removeClass("light-cursor");
}
});
merge_right.forEach(function (item) {
var temp = $(item).parent().find(".CodeMirror-linenumber");
if (
lineNumber_right &&
parseInt($(temp).text()) >= parseInt(lineNumber_right) &&
parseInt($(temp).text()) <= parseInt(lineNumber_right_end)
) {
$(item).addClass("light-cursor");
} else {
$(item).removeClass("light-cursor");
}
});
},
总结
提示:这里对文章进行总结:
记录web在线编辑器的~