[monaco editor官網(wǎng)] 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:編輯器自適應(yīng)布局
- formatOnPaste:復(fù)制粘貼的時候格式化
- lineNumbersMinChars:顯示行號的位數(shù)成箫,控制行號顯示的寬度
更多配置
this.monacoEditor = monaco.editor.create(
this.$refs.editor,
{
value: this.value,//值
language: this.language || 'sql',//設(shè)置語言
formatOnPaste: true,//復(fù)制粘貼的時候格式化
theme: 'vs-dark',//設(shè)置主題
tabSize: 4,//縮進
fontFamily: '微軟雅黑',//字體
automaticLayout: true,//編輯器自適應(yīng)布局
overviewRulerBorder: false,
scrollBeyondLastLine: false,//滾動配置谬俄,溢出才滾動
lineNumbersMinChars: 3,//顯示行號的位數(shù)
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())
})
監(jiān)聽值變化
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: [':'] //觸發(fā)智能提示關(guān)鍵詞
}))
容易出現(xiàn)的bug
- 智能提示重復(fù)
原因:monaco.languages.registerCompletionItemProvider注冊時婴栽,由于monaco.languages為全局對象坐搔,重復(fù)實現(xiàn)實例内列,導(dǎo)致智能提示重復(fù)
解決辦法:
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引用報錯
解決辦法:可參考應(yīng)用2钠乏,手動引用
vue應(yīng)用
1.SQL Editor
- 支持sql語言
- 支持格式化
- 支持智能提示
- 支持placeholder
- 支持獲取選中內(nèi)容
- 支持自定義主題
- 支持監(jiān)聽值變化
<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
// 創(chuàng)建
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()
// 監(jiān)聽變化
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,自定義語言
- 支持格式化
- 支持智能提示
- 支持自定義語言
- 支持失焦更新值
- 支持編輯器自適應(yīng)
<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 })
}
/* 創(chuàng)建編輯器 */
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>