把CodeMirror换成MonacoEditor

This commit is contained in:
jmal 2020-05-25 18:02:16 +08:00
parent 66c93f4cb9
commit d346e55bd7
8 changed files with 302 additions and 280 deletions

View File

@ -32,6 +32,9 @@
"jshint": "^2.11.1",
"lib-flexible": "^0.3.2",
"mavon-editor": "^2.7.7",
"monaco-editor-vue": "^1.0.10",
"monaco-editor-webpack-plugin": "^1.9.0",
"monaco-languages": "^1.10.0",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",

View File

@ -0,0 +1,147 @@
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
require('monaco-languages/release/min/java/java.js')
function noop() { }
export { monaco };
export default {
name: 'MonacoEditor',
props: {
diffEditor: { type: Boolean, default: false }, //是否使用diff模式
width: {type: [String, Number], default: '100%'},
height: {type: [String, Number], default: '100%'},
original: String, //只有在diff模式下有效
value: String,
language: {type: String, default: 'javascript'},
theme: {type: String, default: 'vs'},
options: {type: Object, default() {return {};}},
editorMounted: {type: Function, default: noop},
editorBeforeMount: {type: Function, default: noop}
},
watch: {
options: {
deep: true,
handler(options) {
this.editor && this.editor.updateOptions(options);
}
},
value() {
this.editor && this.value !== this._getValue() && this._setValue(this.value);
},
language() {
// console.log(monaco.languages.getLanguages(),this.editor.getModel(),this.language)
if(!this.editor) return;
if(this.diffEditor){ //diff模式下更新language
const { original, modified } = this.editor.getModel();
monaco.editor.setModelLanguage(original, this.language);
monaco.editor.setModelLanguage(modified, this.language);
}else{
monaco.editor.setModelLanguage(this.editor.getModel(), this.language);
}
},
theme() {
this.editor && monaco.editor.setTheme(this.theme);
},
style() {
this.editor && this.$nextTick(() => {
this.editor.layout();
});
}
},
computed: {
style() {
return {
width: !/^\d+$/.test(this.width) ? this.width : `${this.width}px`,
height: !/^\d+$/.test(this.height) ? this.height : `${this.height}px`
}
}
},
mounted () {
this.initMonaco();
},
beforeDestroy() {
this.editor && this.editor.dispose();
},
render (h) {
return (
<div class="monaco_editor_container" style={this.style}></div>
);
},
methods: {
initMonaco() {
const { value, language, theme, options } = this;
Object.assign(options, this._editorBeforeMount()); //编辑器初始化前
this.editor = monaco.editor[this.diffEditor ? 'createDiffEditor' : 'create'](this.$el, {
value: value,
language: language,
theme: theme,
...options
});
this.diffEditor && this._setModel(this.value, this.original);
this._editorMounted(this.editor); //编辑器初始化后
},
_getEditor() {
if(!this.editor) return null;
return this.diffEditor ? this.editor.modifiedEditor : this.editor;
},
_setModel(value, original) { //diff模式下设置model
const { language } = this;
const originalModel = monaco.editor.createModel(original, language);
const modifiedModel = monaco.editor.createModel(value, language);
this.editor.setModel({
original: originalModel,
modified: modifiedModel
});
},
_setValue(value) {
let editor = this._getEditor();
if(editor) return editor.setValue(value);
},
_getValue() {
let editor = this._getEditor();
if(!editor) return '';
return editor.getValue();
},
_editorBeforeMount() {
const options = this.editorBeforeMount(monaco);
return options || {};
},
_editorMounted(editor) {
this.editorMounted(editor, monaco);
if(this.diffEditor){
editor.onDidUpdateDiff((event) => {
const value = this._getValue();
this._emitChange(value, event);
});
}else{
editor.onDidChangeModelContent(event => {
const value = this._getValue();
this._emitChange(value, event);
});
}
},
_emitChange(value, event) {
this.$emit('change', value, event);
this.$emit('input', value);
}
}
}

View File

@ -1,202 +0,0 @@
<template>
<div class="vue-codemirror-wrap">
<textarea @keydown="onKeyDown"></textarea>
</div>
</template>
<script>
// 加载codemirror/mode目录下所有js文件
const modulesFiles = require.context("codemirror/mode", true, /\.js$/);
modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
const value = modulesFiles(modulePath);
modules[moduleName] = value.default;
return modules;
}, {});
// require('codemirror/mode/xml/xml.js')
// require('codemirror/mode/javascript/javascript.js')
// require('codemirror/mode/css/css.js')
// require('codemirror/mode/shell/shell.js')
// require('codemirror/mode/yaml/yaml.js')
// require('codemirror/mode/swift/swift.js')
// require('codemirror/mode/sql/sql.js')
// require('codemirror/mode/sass/sass.js')
// require('codemirror/mode/sas/sas.js')
// require('codemirror/mode/php/php.js')
// require('codemirror/mode/css/css.js')
// require('codemirror/mode/markdown/markdown.js')
// require('codemirror/mode/go/go.js')
// require('codemirror/mode/dart/dart.js')
// require('codemirror/mode/clike/clike.js')
// 语法警告
// 加载codemirror/addon/hint目录下所有js文件
window.JSHINT = require('jshint').JSHINT;
require('codemirror/addon/lint/lint.js')
require('codemirror/addon/lint/lint.css')
require('codemirror/addon/lint/javascript-lint.js')
// require('codemirror/addon/lint/json-lint.js')
// require('codemirror/addon/lint/html-lint.js')
// const modulesFiles1 = require.context("codemirror/addon/lint", true, /\.js$/);
// modulesFiles1.keys().reduce((modules, modulePath) => {
// const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
// const value = modulesFiles1(modulePath);
// modules[moduleName] = value.default;
// return modules;
// }, {});
// theme
require('codemirror/theme/idea.css')
require('codemirror/theme/ayu-dark.css')
require('codemirror/theme/3024-day.css')
require('codemirror/theme/3024-night.css')
require('codemirror/theme/ambiance.css')
require('codemirror/theme/seti.css')
require('codemirror/theme/darcula.css')
// 支持代码折叠
require('codemirror/addon/fold/foldgutter.css')
require('codemirror/addon/fold/foldcode.js')
require('codemirror/addon/fold/foldgutter.js')
require('codemirror/addon/fold/brace-fold.js')
require('codemirror/addon/fold/comment-fold.js')
// 括号高亮
require('codemirror/addon/edit/matchbrackets.js')
// 全屏模式
require('codemirror/addon/display/fullscreen.css')
require('codemirror/addon/display/fullscreen.js')
// 自动补全
require('codemirror/addon/hint/anyword-hint.js')
require('codemirror/addon/hint/css-hint.js')
require('codemirror/addon/hint/html-hint.js')
require('codemirror/addon/hint/javascript-hint.js')
require('codemirror/addon/hint/show-hint.css')
require('codemirror/addon/hint/show-hint.js')
require('codemirror/addon/hint/sql-hint.js')
require('codemirror/addon/hint/xml-hint.js')
// Crtl-F 搜索
import 'codemirror/addon/scroll/annotatescrollbar.js'
import 'codemirror/addon/search/matchesonscrollbar.js'
import 'codemirror/addon/search/match-highlighter.js'
import 'codemirror/addon/search/jump-to-line.js'
import 'codemirror/addon/dialog/dialog.js'
import 'codemirror/addon/dialog/dialog.css'
import 'codemirror/addon/search/searchcursor.js'
import 'codemirror/addon/search/search.js'
let CodeMirror = require('codemirror/lib/codemirror.js')
require('codemirror/lib/codemirror.css')
export default {
name: 'VueCodemirror',
props: {
value: {
type: String,
default: ''
},
options: {
type: Object,
default: function () {
return {
mode: 'text/javascript',
lineNumbers: true,
lineWrapping: true,
// foldGutter: true, // 启用行槽中的代码折叠
// // 在行槽中添加行号显示器、折叠器、语法检测器
// gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
// lint: true,
}
}
},
},
data: function () {
return {
skipNextChangeEvent: false,
nowValue: ''
}
},
beforeMount(){
this.onKeyDown = this.onKeyDown.bind(this);
document.addEventListener('keydown', this.onKeyDown);
},
mounted: function () {
let that = this
this.editor = CodeMirror.fromTextArea(this.$el.querySelector('textarea'), this.options)
this.editor.setValue(this.value)
this.editor.on('change', function(cm) {
if (that.skipNextChangeEvent) {
that.skipNextChangeEvent = false
return
}
that.nowValue = cm.getValue()
if (!!that.$emit) {
that.$emit('change', cm.getValue())
that.$emit('input', cm.getValue())
}
})
console.log(this.editor)
this.$nextTick(()=>{
this.editor.clearHistory()
})
},
watch: {
'value': function (newVal, oldVal) {
var editorValue = this.editor.getValue()
if (newVal !== editorValue) {
this.skipNextChangeEvent = true
// var scrollInfo = this.editor.getScrollInfo()
// this.editor.refresh()
this.editor.setValue(newVal)
this.editor.scrollTo(0, 0)
// this.editor.scrollTo(scrollInfo.left, scrollInfo.top)
}
this.editor.clearHistory()
},
'options': function (newOptions, oldVal) {
if (typeof newOptions === 'object') {
for (const optionName in newOptions) {
if (newOptions.hasOwnProperty(optionName)) {
this.editor.setOption(optionName, newOptions[optionName])
}
}
}
}
},
methods: {
onKeyDown(event) {
const isMac = navigator.platform.startsWith('Mac');
const {key, code, keyCode, ctrlKey, metaKey} = event;
const isCmd = isMac && metaKey || !isMac && ctrlKey;
if (!isCmd) {
return;
}
const isS = key === 's' || code === 'KeyS' || keyCode === 83;
if (isS && this.editor) {
if(this.nowValue !== ''){
this.$emit('save', this.nowValue)
}
event.stopPropagation();
event.preventDefault();
}
},
},
beforeDestroy: function () {
if (this.editor) {
this.editor.toTextArea()
}
}
}
</script>
<style>
.CodeMirror-code {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
</style>

View File

@ -6,8 +6,8 @@
@inited="inited"
class="viewer" ref="viewer"
>
<template scope="c">
<div v-for="src in c.images" :key="src">
<template slot-scope="scope">
<div v-for="src in scope.images" :key="src">
<!-- <video v-if="src.contentType === 'video'" :src="src.url"> -->
<!-- <video v-if="src.contentType === 'video'" controls="" name="media"><source :src="src.url" type="video/mp4"></video> -->
<img :src="src">

View File

@ -14,32 +14,32 @@
</div>
</div>
<div class="content">
<vue-code-mirror
<MonacoEditor
width="900"
height="640"
theme="vs"
:language="language"
:diffEditor="diffEditor"
original="..."
:value="content"
:options="options"
ref="myEditor"
@change="change"
@save="save"
>
</vue-code-mirror>
></MonacoEditor>
</div>
</el-dialog>
</template>
<script>
import { modeInfo,lineWrapping } from '@/utils/file-type'
let CodeMirror = require('codemirror/lib/codemirror.js')
import '@/utils/directives.js'
import api from '@/api/file-api.js'
import markdownApi from '@/api/markdown-api'
import VueCodeMirror from '@/components/VueCodemirror/VueCodeMirror'
import MonacoEditor from '../MonacoEditorVue'
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
export default {
name: "SimTextPreview",
components: {
VueCodeMirror,
MonacoEditor
},
props: {
file: {
@ -55,34 +55,13 @@
}
}
},
computed: {
options: function () {
return {
mode: this.codeMode,// 代码高亮
smartIndent: true, // 智能缩进
indentUnit: 4, // 智能缩进单位为4个空格长度
indentWithTabs: true, // 使用制表符进行智能缩进
styleActiveLine: true, // 显示选中行的样式
lineNumbers: true, //显示行号
lineWrapping: this.lineWrapping, //代码换行显示
// foldGutter: true, // 启用行槽中的代码折叠
// // 在行槽中添加行号显示器、折叠器、语法检测器
// gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
lint: true,
readOnly: this.readOnly,
theme: 'darcula',
// theme: 'default',
fullScreen: false, // 全屏
matchBrackets: true, // 括号匹配
hintOptions: {
completeSingle: false
},
extraKeys: {'ctrl-space': 'autocomplete'}, //智能提示
}
}
},
data(){
return{
language: 'html',
options: {
},
diffEditor: false,
textPreviewVisible: false,
transformX: (document.body.clientWidth-900)/2,
transformY: (document.body.clientHeight-640)/2,
@ -106,24 +85,15 @@
dangerouslyUseHTMLString: true,
message: '<span>&nbsp;&nbsp;正在加载数据...</span>'
})
this.codeMode = 'text/javascript'
this.codeMode = 'javascript'
let suffix = a.suffix
const modeIndex = modeInfo.findIndex(item => item.ext && item.ext.includes(suffix))
if(modeIndex > -1){
const mode = modeInfo[modeIndex].mode
if(CodeMirror.modes.hasOwnProperty(mode)){
let mime = modeInfo[modeIndex].mime
if(!mime){
mime = modeInfo[modeIndex].mimes[0]
}
this.codeMode = mime
}else{
this.$message.warning("暂不支持的文件类型:",modeInfo[modeIndex].mime)
}
}
if(lineWrapping.includes(suffix)){
this.lineWrapping = true
let languages = monaco.languages.getLanguages();
const languagesIndex = languages.findIndex(item => item.extensions && item.extensions.includes('.'+suffix))
if(languagesIndex > -1){
const language = languages[languagesIndex].id
console.log(language)
this.language = language
}
api.previewText({
id: a.id,

View File

@ -166,18 +166,18 @@ export const constantRoutes = [
]
},
// {
// path: '/demo',
// component: Layout,
// children: [
// {
// path: 'index',
// name: 'demo',
// component: () => import('@/views/demo/index'),
// meta: { title: 'demo', icon: 'example' }
// }
// ]
// },
{
path: '/demo',
component: Layout,
children: [
{
path: 'index',
name: 'demo',
component: () => import('@/views/demo/index'),
meta: { title: 'demo', icon: 'example' }
}
]
},
{
path: '/markdown',

View File

@ -1,24 +1,105 @@
<template>
<div>
<button-upload></button-upload>
<div id="app">
<MonacoEditor
width="800"
height="500"
theme="vs-dark"
language="json"
:diffEditor="diffEditor"
original="..."
:value="value"
:options="options"
@change="onChange"
></MonacoEditor>
</div>
</template>
<script>
import ButtonUpload from "@/components/button/ButtonUpload";
import { MonacoEditor,monaco} from 'monaco-editor-vue';
export default {
name: "App",
components: {
ButtonUpload
MonacoEditor
},
data() {
return {
options: {
//Monaco Editor Options
},
diffEditor: false,
value: "{\n" +
"\t\"type\": \"team\",\n" +
"\t\"test\": {\n" +
"\t\t\"testPage\": \"tools/testing/run-tests.htm\",\n" +
"\t\t\"enabled\": true\n" +
"\t},\n" +
" \"search\": {\n" +
" \"excludeFolders\": [\n" +
"\t\t\t\".git\",\n" +
"\t\t\t\"node_modules\",\n" +
"\t\t\t\"tools/bin\",\n" +
"\t\t\t\"tools/counts\",\n" +
"\t\t\t\"tools/policheck\",\n" +
"\t\t\t\"tools/tfs_build_extensions\",\n" +
"\t\t\t\"tools/testing/jscoverage\",\n" +
"\t\t\t\"tools/testing/qunit\",\n" +
"\t\t\t\"tools/testing/chutzpah\",\n" +
"\t\t\t\"server.net\"\n" +
" ]\n" +
" },\n" +
"\t\"languages\": {\n" +
"\t\t\"vs.languages.typescript\": {\n" +
"\t\t\t\"validationSettings\": [{\n" +
"\t\t\t\t\"scope\":\"/\",\n" +
"\t\t\t\t\"noImplicitAny\":true,\n" +
"\t\t\t\t\"noLib\":false,\n" +
"\t\t\t\t\"extraLibs\":[],\n" +
"\t\t\t\t\"semanticValidation\":true,\n" +
"\t\t\t\t\"syntaxValidation\":true,\n" +
"\t\t\t\t\"codeGenTarget\":\"ES5\",\n" +
"\t\t\t\t\"moduleGenTarget\":\"\",\n" +
"\t\t\t\t\"lint\": {\n" +
" \"emptyBlocksWithoutComment\": \"warning\",\n" +
" \"curlyBracketsMustNotBeOmitted\": \"warning\",\n" +
" \"comparisonOperatorsNotStrict\": \"warning\",\n" +
" \"missingSemicolon\": \"warning\",\n" +
" \"unknownTypeOfResults\": \"warning\",\n" +
" \"semicolonsInsteadOfBlocks\": \"warning\",\n" +
" \"functionsInsideLoops\": \"warning\",\n" +
" \"functionsWithoutReturnType\": \"warning\",\n" +
" \"tripleSlashReferenceAlike\": \"warning\",\n" +
" \"unusedImports\": \"warning\",\n" +
" \"unusedVariables\": \"warning\",\n" +
" \"unusedFunctions\": \"warning\",\n" +
" \"unusedMembers\": \"warning\"\n" +
" }\n" +
"\t\t\t}, \n" +
"\t\t\t{\n" +
"\t\t\t\t\"scope\":\"/client\",\n" +
"\t\t\t\t\"baseUrl\":\"/client\",\n" +
"\t\t\t\t\"moduleGenTarget\":\"amd\"\n" +
"\t\t\t},\n" +
"\t\t\t{\n" +
"\t\t\t\t\"scope\":\"/server\",\n" +
"\t\t\t\t\"moduleGenTarget\":\"commonjs\"\n" +
"\t\t\t},\n" +
"\t\t\t{\n" +
"\t\t\t\t\"scope\":\"/build\",\n" +
"\t\t\t\t\"moduleGenTarget\":\"commonjs\"\n" +
"\t\t\t},\n" +
"\t\t\t{\n" +
"\t\t\t\t\"scope\":\"/node_modules/nake\",\n" +
"\t\t\t\t\"moduleGenTarget\":\"commonjs\"\n" +
"\t\t\t}],\n" +
"\t\t\t\"allowMultipleWorkers\": true\n" +
"\t\t}\n" +
"\t}\n" +
"}",
}
},
mounted () {
},
methods: {
onChange(value) {
}
}
}
};
</script>
<style>
</style>

View File

@ -16,7 +16,23 @@ const name = defaultSettings.title || 'jmalCloud' // page title
const port = process.env.port || process.env.npm_config_port || 9528 // dev port
//所有配置项说明均可在https://cli.vuejs.org/config/中找到
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
// let monacoLanguages = []
// const modulesFiles = require.context("monaco-languages/release/dev/", true, /\.js$/)
// modulesFiles.keys().reduce((modules, modulePath) => {
// const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
// if(moduleName.indexOf('contribution') === -1){
// monacoLanguages.push(moduleName.replace(/([/][^/]+)$/, ""))
// }
// // const value = modulesFiles(modulePath);
// // modules[moduleName] = value.default;
// // return modules;
// }, {});
module.exports = {
/**
*如果您打算在子路径下部署网站,则需要设置publicPath,
*例如GitHub Pages。如果您打算将网站部署到https://foo.github.io/bar/,
@ -78,6 +94,13 @@ module.exports = {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
config.plugin('monaco-editor').use(MonacoWebpackPlugin, [
{
// Languages are loaded on demand at runtime
languages: ["abap", "apex", "azcli", "bat", "cameligo", "clojure", "coffee", "cpp", "csharp", "csp", "css", "dockerfile", "fsharp", "go", "graphql", "handlebars", "html", "ini", "java", "javascript", "kotlin", "less", "lua", "markdown", "mips", "msdax", "mysql", "objective-c", "pascal", "pascaligo", "perl", "pgsql", "php", "postiats", "powerquery", "powershell", "pug", "python", "r", "razor", "redis", "redshift", "restructuredtext", "ruby", "rust", "sb", "scheme", "scss", "shell", "solidity", "sophia", "sql", "st", "swift", "tcl", "twig", "typescript", "vb", "xml", "yaml"]
}
])
// set svg-sprite-loader
config.module
.rule('svg')