[monaco editor官网] https://microsoft.github.io/monaco-editor/index.html
[github] https://github.com/Microsoft/monaco-editor
初始化安装
npm install monaco-editor --save
npm install monaco-editor-webpack-plugin --save-dev
webpack.base.conf.js添加配置(或vue.config.js添加配置)
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
...
module.exports = {
...
plugins: [
...
new MonacoWebpackPlugin()
]
};
vue.config.js添加配置(或webpack.base.conf.js添加配置)
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
/* editor */
config.plugin('monaceditor')
.use(MonacoWebpackPlugin, [{
languages: ['json', 'sql', 'javascript', 'typescript'] //项目所使用的语言
}])
常用配置
- readOnly:是否已读
- minimap:是否显示索引地图
- automaticLayout:编辑器自适应布局
- formatOnPaste:复制粘贴的时候格式化
- lineNumbersMinChars:显示行号的位数,控制行号显示的宽度
更多配置
this.monacoEditor = monaco.editor.create(
this.$refs.editor,
{
value: this.value,//值
language: this.language || 'sql',//设置语言
formatOnPaste: true,//复制粘贴的时候格式化
theme: 'vs-dark',//设置主题
tabSize: 4,//缩进
fontFamily: '微软雅黑',//字体
automaticLayout: true,//编辑器自适应布局
overviewRulerBorder: false,
scrollBeyondLastLine: false,//滚动配置,溢出才滚动
lineNumbersMinChars: 3,//显示行号的位数
minimap: { enabled: this.minimap }//是否显示索引地图
}
)
更新配置
this.monacoEditor.updateOptions({ readOnly: value })
获取值和赋值
//获取值
this.monacoEditor.getValue()
//赋值
this.monacoEditor.setValue(this.value)
格式化
- sql语言格式化
- 引入插件sql-formatter
//安装 npm i sql-formatter
import sqlFormatter from 'sql-formatter'
this.monacoEditor.setValue(sqlFormatter.format(this.value))
- json,javascript等语言格式化
// 初始化格式化需要添加setTimeout,格式化才有效;非初始化则不需要
setTimeout(() => {
this.monacoEditor.getAction('editor.action.formatDocument').run()
}, 500)
失焦时更新值
this.monacoEditor.onDidBlurEditorText(() => {
this.$emit('update', this.monacoEditor.getValue())
})
监听值变化
this.monacoEditor.onDidChangeModelContent(e => {
this.$emit('update', this.monacoEditor.getValue()) //使value和其值保持一致
}
智能提示
const suggestions = [{
label: 'simpleText',//展示
insertText: 'simpleText'//实际输入到editor
}, {
label: 'simpleText1',
insertText: 'simpleText1'
}]
monaco.languages.registerCompletionItemProvider(this.language, {
provideCompletionItems: () => {
return { suggestions: suggestions }
},
triggerCharacters: [':'] //触发智能提示关键词
}))
容易出现的bug
- 智能提示重复
原因:monaco.languages.registerCompletionItemProvider注册时,由于monaco.languages为全局对象,重复实现实例,导致智能提示重复
解决办法:
1.一种语言只注册一次
2.失焦时将suggestions_ = [],聚焦时suggestions_ = suggestions
this.monacoEditor.onDidBlurEditorText(() => {
this.suggestions_ = [] // 解决多个编辑器suggestions缓存问题
this.$emit('update', this.monacoEditor.getValue())
})
this.monacoEditor.onDidFocusEditorText(() => {
this.suggestions_ = this.suggestions // 解决多个编辑器suggestions缓存问题
})
- 使用json语言,worker.js引用报错
解决办法:可参考应用2,手动引用
vue应用
1.SQL Editor
- 支持sql语言
- 支持格式化
- 支持智能提示
- 支持placeholder
- 支持获取选中内容
- 支持自定义主题
- 支持监听值变化
<template>
<div class="sql-panel">
<span class="sql-placeholder" v-if="placeholderShow" @click="focusEdit">请输入sql语句</span>
<div ref="sqlEditor" class="sql-editor" :style="{height: height}"></div>
</div>
</template>
<script>
import * as monaco from 'monaco-editor'
import sqlFormatter from 'sql-formatter'
/* const suggestions = [{
label: 'simpleText',
insertText: 'simpleText'
}, {
label: 'simpleText1',
insertText: 'simpleText1'
}] */
export default {
name: 'SqlEditor',
inject: ['eventBus'],
props: {
value: String,
height: String
},
model: {
prop: 'value', // 绑定的值,通过父组件传递
event: 'update' // 自定义事件名
},
data () {
return {
value_: this.value,
suggestions: [],
placeholderShow: false,
lineValue: '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
}
},
created () {
this.eventBus.$on('beautify-sql', this.beautifySql)
window.onresize = () => {
this.height_ = this.getHeight()
}
},
mounted () {
setTimeout(() => {
this.creatMonacoEditor()
}, 200)
},
methods: {
getHeight () {
return (window.innerHeight - 195) + 'px'
},
/* 智能提示 */
async provideCompletionItems (model, position, context, token) {
monaco.languages.isRegistered = true
let linesContent = model.getLinesContent().slice(0, this.endLineNumber)
linesContent[this.endLineNumber - 1] = linesContent[this.endLineNumber - 1].slice(0, this.endColumn)
await this.getSuggestions(linesContent.join(''))
return { suggestions: this.suggestions }
},
getSuggestions (sql) {
return this.$api['dataModelAdd/getSuggestions']({
sqlSentences: sql
}).then((data) => {
this.suggestions = data || []
})
},
beautifySql () {
this.monacoEditor.setValue(sqlFormatter.format(this.value || '', {
indent: ' '
}))
},
setValue (value) {
this.monacoEditor.setValue(sqlFormatter.format(value))
},
getSelection () {
if (this.monacoEditor) {
let selection = this.monacoEditor.getSelection()
return this.monacoEditor.getModel().getValueInRange(selection)
}
return this.value
},
creatMonacoEditor () {
if (this.monacoEditor) return
// 创建
this.monacoEditor = monaco.editor.create(
this.$refs.sqlEditor,
{
value: this.value || this.lineValue,
language: 'sql',
tabSize: 4,
fontFamily: '微软雅黑',
automaticLayout: true,
overviewRulerBorder: false,
scrollBeyondLastLine: false,
minimap: { enabled: false }
}
)
this.setPlaceholder(this.value === '')
this.setTheme()
// 监听变化
this.monacoEditor.onDidChangeModelContent(e => {
this.endColumn = e.changes[0].range.endColumn
this.endLineNumber = e.changes[0].range.endLineNumber
this.value_ = this.monacoEditor.getValue()
this.setPlaceholder(e.changes[0].text === '' && this.value_.trim() === '')
this.$emit('update', this.value_)
})
// 提示
monaco.languages.isRegistered || monaco.languages.registerCompletionItemProvider('sql', {
provideCompletionItems: this.provideCompletionItems
})
},
focusEdit () {
this.monacoEditor.focus()
},
setPlaceholder (show) {
this.placeholderShow = show
show && this.monacoEditor.focus()
},
setTheme () {
monaco.editor.defineTheme('myTheme', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editor.lineHighlightBackground': '#fff'
}
})
monaco.editor.setTheme('myTheme')
}
}
}
</script>
<style lang="less" scoped>
.sql-panel{
position: relative;
}
.sql-placeholder{
position: absolute;
left: 75px;
top: 0;
z-index: 1;
line-height: 24px;
font-family: '微软雅黑'
}
</style>
2.多语言Editor
- 支持json,sql,javascript,自定义语言
- 支持格式化
- 支持智能提示
- 支持自定义语言
- 支持失焦更新值
- 支持编辑器自适应
<template>
<div ref="editor" class="editor" style="height:100%;"></div>
</template>
<script>
import Vue from 'vue'
import sqlFormatter from 'sql-formatter'
const CUSTOMLANGUAGE = 'custom'
export default {
props: {
value: String,
language: String,
readOnly: {
type: Boolean,
default: false
},
minimap: {
type: Boolean,
default: false
},
suggestions: Array
},
model: {
prop: 'value',
event: 'update'
},
watch: {
value: {
handler (value) {
this.monacoEditor && this.format()
},
immediate: true
},
minimap: {
handler (value) {
this.updateOptions({ minimap: value })
},
immediate: true
},
readOnly: {
handler (value) {
this.updateOptions({ readOnly: value })
},
immediate: true
}
},
mounted () {
this.creatMonacoEditor()
},
methods: {
creatMonacoEditor () {
// if (this.monacoEditor) return
import(/* webpackChunkName: "monaco-editor" */ /* webpackMode: "lazy" */ 'monaco-editor').then((monaco) => {
// MonacoEnvironment定义要放到这里,不然会被覆盖
window.MonacoEnvironment = {
getWorkerUrl (moduleId, label) {
if (label === 'json') {
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
importScripts('存放路径/json.worker.js');`
)}`
}
if (label === 'javascript') {
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
importScripts('存放路径/typescript.worker.js');`
)}`
}
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
importScripts('存放路径/editor.worker.js');`
)}`
}
}
/* 初始化自定义语言 */
if (this.language === CUSTOMLANGUAGE && !monaco.languages.customLanguage) {
monaco.languages.customLanguage = true
monaco.languages.register({ id: this.language })
}
/* 创建编辑器 */
this.monacoEditor = monaco.editor.create(
this.$refs.editor,
{
value: this.value,
language: this.language || 'sql',
formatOnPaste: true,
theme: 'vs-dark',
tabSize: 4,
fontFamily: '微软雅黑',
automaticLayout: true,
overviewRulerBorder: false,
scrollBeyondLastLine: false,
lineNumbersMinChars: 3,
minimap: { enabled: this.minimap }
}
)
this.initFormat()
this.monacoEditor.onDidBlurEditorText(() => {
this.$emit('update', this.monacoEditor.getValue())
})
monaco.languages.isRegistered === this.language || (this.suggestions &&
this.suggestions.length && monaco.languages.registerCompletionItemProvider(this.language, {
provideCompletionItems: () => {
monaco.languages.isRegistered = this.language
return { suggestions: _.cloneDeep(this.suggestions) }
},
triggerCharacters: [':']
}))
})
},
initFormat () {
if (this.language === CUSTOMLANGUAGE) {
return
}
this.language === 'sql' ? this.sqlFormat() : setTimeout(() => {
this.monacoEditor.getAction('editor.action.formatDocument').run()
.then(() => {
this.monacoEditor.updateOptions({ readOnly: this.readOnly })
})
}, 500)
},
format () {
if (this.language === CUSTOMLANGUAGE) {
this.monacoEditor.setValue(this.value)
return
}
this.language === 'sql' ? this.sqlFormat() : this.jsonFormat()
},
sqlFormat () {
this.monacoEditor.setValue(sqlFormatter.format(this.value))
},
jsonFormat () {
this.monacoEditor.setValue(this.value)
this.monacoEditor.updateOptions({ readOnly: false })
this.monacoEditor.getAction('editor.action.formatDocument').run()
.then(() => {
this.monacoEditor.updateOptions({ readOnly: this.readOnly })
})
},
updateOptions (opts) {
this.monacoEditor && this.monacoEditor.updateOptions(opts)
}
}
}
</script>