目录
前言一、monaco-editor1、编辑器源码2、对比diff2、体积优化 二、ace-editor?1、源码2、体积优化 3、codemirror1、diff 总结
前言
提示:这里可以添加本文要记录的大概内容:
提示:以下是本篇文章正文内容,下面案例可供参考
一、monaco-editor
“monaco-editor”: “^0.30.1”
点击进入api地址
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在线编辑器的~