600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 在vue中实现代码编辑器(lua) - ace/codemirror/monaco-editor

在vue中实现代码编辑器(lua) - ace/codemirror/monaco-editor

时间:2018-09-18 19:04:49

相关推荐

在vue中实现代码编辑器(lua) - ace/codemirror/monaco-editor

# 前言

由于项目需求,需要完成一个支持代码提示、代码校验、代码格式化的lua编辑器

1 技术选型

精力有限,只了解了几个主流的编辑器codemirror、ace、Monaco Editor

codemirror5

用户最多,生态最好,所以插件也相对完备,能想到的基本都有,同时也被很多线上应用在用,有什么问题百度搜下基本都能搜到。

github官网官方lua的demo

ace

ajax团队弄的一个开源编辑器,生态、文档也还不错,相对codemirror来说,语法提示和语法校验支持的语言种类更多

官网github官方在线测试demo

Monaco Editor

微软开源的一个web代码编辑器,可以看作vscode的web版,各项功能都比较强,理论上来说vscode支持的插件Monaco Editor也支持(因为vscode的代码编辑器也是用这个实现的),但是相关文档比较少

github在线测试demo

基于vue封装的组件

vue-codemirrorvue2-ace-editor(不推荐使用,已长期未维护)vue-monaco-editor(不推荐使用,已长期未维护

对比

各个编辑器对JavaScript、SQL、Java这些热门语言的支持度都很高,其他相对冷门语言的支持度都差一点,可以看看以下文章来帮助大家选型(以上对比图片来源于以下文章):

Wikipedia:基于 JavaScript 的源代码编辑器的比较基于JavaScript的代码编辑器的比较和选型

知乎的这个问题:CodeMirror 和 ACE 相比各有什么优缺点?浅显的Monaco Editor 与codemirror 选型

2 技术验证

codemirror.js(vue-codemirror)

由于之前有了解过codemirror,所以第一个方案自然就尝试使用codemirror来实现lua编辑器

注意:

codemirror已发布v6版本,本文使用的是v5版本vue-codemirror已兼容codemirror@6版本,只有@4版本才支持codemirror@5

安装

推荐使用vue-codemirror

npm i codemirror@5 -Snpm i vue-codemirror@4.0.6 -S

推荐安装@types/codemirror以支持类型推断

npm i @types/codemirror -D

基础使用

注册全局组件

// require libimport Vue from 'vue'import VueCodemirror from 'vue-codemirror'// require stylesimport 'codemirror/lib/codemirror.css'// require more codemirror resource...// you can set default global options and events when useVue.use(VueCodemirror, /* { options: { theme: 'base16-dark', ... },events: ['scroll', ...]} */)

注册局部组件

// require componentimport {codemirror } from 'vue-codemirror'// require stylesimport 'codemirror/lib/codemirror.css'// require more codemirror resource...// componentexport default {components: {codemirror}}

使用组件

<template><codemirror v-model="code" :options="cmOptions"></codemirror></template><script>// 引入语言import 'codemirror/mode/javascript/javascript.js'// 引入样式import 'codemirror/theme/base16-dark.css'// 按需导入codemirror其他功能import 'codemirror/xxx'export default {data () {return {code: 'const a = 10',cmOptions: {// codemirror optionstabSize: 4,mode: 'text/javascript',theme: 'base16-dark',lineNumbers: true,line: true,// more codemirror options, 更多 codemirror 的高级配置...}}},methods: {},computed: {codemirror() {return this.$refs.myCm.codemirror}},mounted() {}}</script>

实现lua代码编辑器

使用lua模式时,mode需要设置为text/x-lua

具体功能需要引入哪些模块已标好注释

<template><codemirrorclass="lua-editor"ref="editor":value="value":options="codemirrorOptions"@input="handleInputChange"@input-read="handleInputRead"/></template><script>import {codemirror } from 'vue-codemirror'import 'codemirror/lib/codemirror.css'import 'codemirror/mode/lua/lua'import 'codemirror/theme/neat.css'import 'codemirror/theme/idea.css'// #region 搜索功能// find:Ctrl-F (PC), Cmd-F (Mac)// findNext:Ctrl-G (PC), Cmd-G (Mac)// findPrev:Shift-Ctrl-G (PC), Shift-Cmd-G (Mac)// replace:Shift-Ctrl-F (PC), Cmd-Alt-F (Mac)// replaceAll:Shift-Ctrl-R (PC), Shift-Cmd-Alt-F (Mac)import 'codemirror/addon/dialog/dialog.css'import 'codemirror/addon/dialog/dialog'import 'codemirror/addon/search/searchcursor'import 'codemirror/addon/search/search'import 'codemirror/addon/search/jump-to-line'import 'codemirror/addon/search/matchesonscrollbar'import 'codemirror/addon/search/match-highlighter'// #endregion// #region 代码提示功能// 具体语言可以从 codemirror/addon/hint/ 下引入多个import 'codemirror/addon/hint/show-hint.css'import 'codemirror/addon/hint/show-hint.js'import 'codemirror/addon/hint/anyword-hint' // 简易的代码提示功能// #endregion// #region 高亮行功能import 'codemirror/addon/selection/active-line'import 'codemirror/addon/selection/selection-pointer'// #endregion// #region 覆盖scrollbar样式功能import 'codemirror/addon/scroll/simplescrollbars.css'import 'codemirror/addon/scroll/simplescrollbars'// #endregion// #region 自动括号匹配功能import 'codemirror/addon/edit/matchbrackets.js'// #endregion// 全屏功能 由于项目复杂,自带的全屏功能一般不好使// import 'codemirror/addon/display/fullscreen.css'// import 'codemirror/addon/display/fullscreen.js'// 显示自动刷新import 'codemirror/addon/display/autorefresh.js'// 多语言支持?// import 'codemirror/addon/mode/overlay'// import 'codemirror/addon/mode/multiplex'import 'codemirror/addon/lint/lint.js'import 'codemirror/addon/lint/lint.css'// #region 代码段折叠功能import 'codemirror/addon/fold/foldcode.js'import 'codemirror/addon/fold/foldgutter.js'import 'codemirror/addon/fold/foldgutter.css'import 'codemirror/addon/fold/brace-fold.js' // 括号折叠import 'codemirror/addon/fold/comment-fold.js'import 'codemirror/addon/fold/indent-fold.js' // 缩进折叠import 'codemirror/addon/fold/comment-fold.js'// import 'codemirror/addon/fold/xml-fold.js'// import 'codemirror/addon/fold/markdown-fold.js'// #endregion// #region merge功能// import 'codemirror/addon/merge/merge.css'// import 'codemirror/addon/merge/merge.js'// #endregionexport default {name: 'LuaEditor',// #region 组件基础components: {codemirror: codemirror },props: {value: {type: String,required: true}},// #endregion// #region 数据相关data() {return {/** @type {import('@types/codemirror/index').EditorConfiguration}*/codemirrorOptions: {// 语言及语法模式mode: 'text/x-lua',// 主题theme: 'neat',// 显示函数line: true,// 显示行号lineNumbers: true,// 软换行lineWrapping: true,// tab宽度tabSize: 4,// 允许拖入的文件类型allowDropFileTypes: ['text/x-lua'],cursorScrollMargin: 5,extraKeys: {},// 高亮行功能styleActiveLine: true,// 调整scrollbar样式功能// scrollbarStyle: 'overlay',// 自动括号匹配功能matchBrackets: true,autofocus: true,autoRefresh: true,// #region 代码折叠foldGutter: true,// foldOptions: { scanUp: true },gutters: ['CodeMirror-linenumbers','CodeMirror-foldgutter','CodeMirror-lint-markers'],// #endregionshowHint: true,lint: true,hintOptions: {// 避免由于提示列表只有一个提示信息时,自动填充completeSingle: false}}}},computed: {},watch: {},// #endregion// #region 生命周期created() {},mounted() {},// #endregionmethods: {handleInputChange(value) {this.$emit('input', value)},handleInputRead(cm) {// 显示代码提示框cm.showHint()}}}</script><style lang="scss">.lua-editor {textarea {height: 100%;}}</style>

实现效果

代码高亮:

简易的代码提示:

输入错误的代码:

由于不支持lua语言的代码段折叠、代码校验,所以无法测试

总结

优点:

支持lua语法高亮简易的语法提示(根据当前代码中的关键字)可以按需导入功能与模块

缺点:

不支持lua代码校验不支持lua代码格式化lua语法提示不满足需求没有全量导入功能,当导入大量模块和功能时,需要写很多import,不方便管理

.

.

.

ace.js(本文使用的方案)

安装

npm i ace-builds -S

推荐安装@types/ace以提供类型推断

npm i @types/ace -D

基础使用

全量引入(不推荐,仅开发阶段使用)

ace提供了一个全量导入模块:ace-builds/webpack-resolver

缺点:导致引入包过大,不需要的、使用不到的也被打包进来了,而且打包后的根目录下会有很多js文件优点:不需要再去手动查找/导入会用到的模块了,适合在开发阶段使用

import ace from 'ace-builds'import 'ace-builds/css/ace.css'import 'ace-builds/webpack-resolver' // 全量导入

打包后输出如下:

按需引入(推荐,打包时使用)

ace-build.js建议使用按需引入,全量引入时体积过于庞大

按需引入需要知道会用到哪些模块,提前import进来,适合已开发完成、维护的阶段需要注意的是,worker相关js文件,不能直接使用import导入,一定要使用ace.config.setModuleUrl + file-loader导入,否则会报错误

import ace from 'ace-builds'import 'ace-builds/css/ace.css'// #region lua语法高亮import 'ace-builds/src-noconflict/mode-lua'import 'ace-builds/src-noconflict/snippets/lua'// #endregion// #region 代码提示import 'ace-builds/src-noconflict/ext-language_tools'// #endregion// #region 代码校验ace.config.setModuleUrl('ace/mode/base_worker',require('file-loader?esModule=false!ace-builds/src-noconflict/worker-base.js'))ace.config.setModuleUrl('ace/mode/lua_worker',require('file-loader?esModule=false!ace-builds/src-noconflict/worker-lua.js'))// #endregion// #region 主题import 'ace-builds/src-noconflict/theme-chrome'// #endregion// #region 其他功能import 'ace-builds/src-noconflict/ext-searchbox'import 'ace-builds/src-noconflict/ext-keybinding_menu'import 'ace-builds/src-noconflict/ext-settings_menu'// #endregion

打包后输出如下,少了很多js文件,体积比全量引入小了10+MB

实现Lua代码编辑器

<template><div class="lua-editor"><!-- <textarea ref="textarea"></textarea> --></div></template><script>import ace from 'ace-builds'import 'ace-builds/css/ace.css'import 'ace-builds/webpack-resolver' // 全量导入export default {// #region 组件基础components: {},props: {value: {type: String,required: true},options: Object},// #endregion// #region 数据相关data() {content: this.value || '',return {/** @type {import('ace-builds').Ace.EditorOptions}*/editorOptions: {// keyboardHandler: '',mode: 'ace/mode/lua',theme: 'ace/theme/chrome',tabSize: 2,selectionStyle: 'text',// 拖动代码块dragEnabled: true,useWorker: true,// 自动缩进enableAutoIndent: true,// 显示行号showLineNumbers: true,useSoftTabs: true,// 渐变隐藏折叠按钮fadeFoldWidgets: true,// 输入边界showPrintMargin: false,// 高亮当前行highlightActiveLine: true,// 高亮选中词highlightSelectedWord: true,// 滚动动画autoScrollEditorIntoView: true,copyWithEmptySelection: true,// #region 启用自动完成和代码段// enableBasicAutocompletion: true,enableLiveAutocompletion: true,enableSnippets: true// #endregion}}},computed: {/** @return {import('ace-builds').Ace.Editor} */_editor() {return this.editor}},watch: {value(nval) {this.syncContent(nval)},options(nval) {this.syncOptions(nval)}},// #endregion// #region 生命周期created() {},mounted() {let editor = ace.edit(this.$el, this.editorOptions)this.editor = editor},beforeDestroy() {this._editor.destroy()},// #endregionmethods: {/*** 同步内容*/syncContent(value = this.value) {if (this.content != value) {this._editor?.setValue(value, 1)}},/*** 同步配置*/syncOptions(option = this.option) {let keys = Object.keys(option)for (let key of keys) {// 禁止修改固定的optionif (key in this.editorOptions) continuethis._editor?.setOption(key, option[key])}}}}</script>

实现效果

总结

优点:

支持lua语法高亮支持lua代码校验支持lua代码块折叠基础语法提示,提供部分代码片段可以按需导入功能与模块

缺点:

不支持lua代码格式化引入的模块,打包后会在根目录下产生很多js文件

Monaco Editor

这个编辑器是在写文章的时候看到的,目前还没有尝试使用,后续有时间追加

可参考文档:

JS在线代码编辑器多种方案monaco-editor,vue-monaco-editorVue + monaco-editor

.

.

.

3 代码格式化

codemirror、ace都不支持lua的代码格式化,那只能在github找找使用javascript实现lua格式化的开源插件了,共找到如下开源库

luaparse

此开源库使用javascript语言实现将lua代码字符串分析为ast抽象语法树,但是无法直接使用,需要二次开发,有一定的开发成本

@appguru/luafmt(本文使用的方案)

此开源库基于luaparse库开发,luaparse将lua语言转换为ast树,@appguru/luafmt在此基础上对ast树进行分析,以实现代码格式化

安装&使用

安装

npm i @appguru/luafmt -S

使用

import {ast } from '@appguru/luafmt'import {parse } from 'luaparse'// 创建格式化器实例let formatter = ast.formatter({indent: ' ', // 缩进符号newline: '\n', // 换行符号extra_newlines: true, // 是否额外换行// tabWidth: 2, // tab宽度// useTabs: true,// semi: false, // 是否加分号inline: {// 单行宽度设置block: {max_exp_length: 60},table: {max_field_count: 0,max_field_length: 0}}})// 使用let result = formatter('lua code')

实际效果

原始代码:

格式化后:

lua-format

此库应该是自行实现了ast语法树分析,所以体积会比较大

安装&使用

安装

npm i lua-format -S

使用

import luafmt from 'luamin'let result = luafmt.Beautify(code, {RenameVariables: false, // 重命名变量RenameGlobals: false, // 重命名全局变量SolveMath: false})

实际效果

原始代码:

格式化后:

多次格式化后:

多行注释测试:

luamin

此开源库也是基于luaparse库开发,不过是内部集成的方式,仅支持lua代码压缩,没有其他功能

安装&使用

安装

npm i luamin -S

使用

import luamin from 'luamin'let result = luamin.minify(code)

实际效果

原始代码:

压缩后:

总结

推荐使用@appguru/luafmt,或者基于luaparse自行实现

4 lua代码编辑器

源码

<template><div class="lua-editor"><!-- <textarea ref="textarea"></textarea> --></div></template><script>import ace from 'ace-builds'// import 'ace-builds/webpack-resolver'import 'ace-builds/css/ace.css'// #region lua语法高亮import 'ace-builds/src-noconflict/mode-lua'import 'ace-builds/src-noconflict/snippets/lua'// #endregion// #region 代码提示import 'ace-builds/src-noconflict/ext-language_tools'// #endregion// #region 代码校验// import 'ace-builds/src-noconflict/worker-base.js'// import 'ace-builds/src-noconflict/worker-lua.js'ace.config.setModuleUrl('ace/mode/base_worker',require('file-loader?esModule=false!ace-builds/src-noconflict/worker-base.js'))ace.config.setModuleUrl('ace/mode/lua_worker',require('file-loader?esModule=false!ace-builds/src-noconflict/worker-lua.js'))// #endregion// #region 主题import 'ace-builds/src-noconflict/theme-chrome'// #endregion// #region 其他功能import 'ace-builds/src-noconflict/ext-searchbox'import 'ace-builds/src-noconflict/ext-keybinding_menu'import 'ace-builds/src-noconflict/ext-settings_menu'// #endregionimport {ast } from '@appguru/luafmt'// import { ast } from './appguru'import {parse } from 'luaparse'const events = ['bulr','change','changeSelectionStyle','changeSession','copy','focus','paste','mousemove','mouseup','mousewheel','click']/*** LuaEditor* @author lcm* @createTime * @description*/export default {name: 'LuaEditor',// #region 组件基础components: {},props: {value: {type: String,required: true},options: Object,theme: String,mode: String,readonly: Boolean},// #endregion// #region 数据相关data() {return {content: this.value || '',cursorPos: null,/** @type {import('ace-builds').Ace.EditorOptions}*/editorOptions: {// keyboardHandler: '',mode: 'ace/mode/lua',theme: 'ace/theme/chrome',tabSize: 2,selectionStyle: 'text',// 拖动代码块dragEnabled: true,useWorker: true,// 自动缩进enableAutoIndent: true,// 显示行号showLineNumbers: true,useSoftTabs: true,// 渐变隐藏折叠按钮fadeFoldWidgets: true,// 输入边界showPrintMargin: false,// 高亮当前行highlightActiveLine: true,// 高亮选中词highlightSelectedWord: true,// 滚动动画autoScrollEditorIntoView: true,copyWithEmptySelection: true,// #region 启用自动完成和代码段// enableBasicAutocompletion: true,enableLiveAutocompletion: true,enableSnippets: true// #endregion}}},computed: {/** @return {import('ace-builds').Ace.Editor} */_editor() {return this.editor}},watch: {value(nval) {this.syncContent(nval)},theme(nval) {this._editor?.setTheme('ace/theme/' + nval)},mode(nval) {this._editor?.getSession().setMode('ace/mode/' + nval)},readonly(nval) {this._editor?.setReadOnly(nval)},options(nval) {this.syncOptions(nval)}},// #endregion// #region 生命周期created() {},mounted() {if (this.theme) {this.editorOptions.theme = this.theme}// if (this.lang) {// this.editorOptions.mode = this.lang// }//@appguru/luafmtthis.formatter = ast.formatter({indent: ' ',newline: '\n',extra_newlines: true,// tabWidth: 2,// useTabs: true,// semi: false,inline: {block: {max_exp_length: 60},table: {max_field_count: 0,max_field_length: 0}}})let editor = ace.edit(this.$el, this.editorOptions)editor.setValue(this.value, 1)// editor.setOption('auto', 1)mands.addCommands([{name: 'showSettingsMenu',bindKey: {win: 'Ctrl-q', mac: 'Ctrl-q' },exec(editor) {ace.config.loadModule('ace/ext/settings_menu', function (module) {module.init(editor)editor.showSettingsMenu()})},readOnly: true}])// 快捷键帮助面板mands.addCommand({name: 'showKeyboardShortcuts',bindKey: {win: 'Ctrl-Alt-h', mac: 'Command-Alt-h' },exec(editor) {ace.config.loadModule('ace/ext/keybinding_menu', function (module) {module.init(editor)editor.showKeyboardShortcuts()})}})//格式化mands.addCommand({name: 'formattingCode',bindKey: {win: 'Shift-Alt-F', mac: 'Shift-Alt-F' },exec: (editor) => {this.formattingCode()}})editor.selection.on('changeCursor', (e) => {this.cursorPos = editor.getCursorPosition()this.$emit('changeCursor', e)})editor.on('change', (ev) => {this.cursorPos = editor.getCursorPosition()let value = editor.getValue()this.content = valueconsole.log('cursorPos', this.cursorPos)this.$emit('input', value)})for (let name of events) {editor.on(name, (ev) => {this.$emit(name, ev)})}this.editor = editor},beforeDestroy() {this._editor.destroy()},// #endregionmethods: {/*** 格式化代码*/formattingCode() {try {const srcAST = parse(this.content)ast.fixRanges(srcAST)ast.insertComments(srcAST)let result = this.formatter(srcAST)this.content = resultthis.editor.setValue(this.content)// 获取当前光标位置// let cursorPos = this._editor.getCursorPosition()// this._editor.setValue(result, 0)// 保持光标位置不变// this._editor.navigateTo(cursorPos.row, cursorPos.column)return true} catch (error) {console.log('格式化失败 ', error)return false}},/*** 同步内容*/syncContent(value = this.value) {if (this.content != value) {this._editor?.setValue(value, 1)}},/*** 同步配置*/syncOptions(option = this.option) {let keys = Object.keys(option)for (let key of keys) {// 禁止修改固定的optionif (key in this.editorOptions) continuethis._editor?.setOption(key, option[key])}}}}</script><style lang="scss">.lua-editor {textarea {height: 100%;}}</style

效果

# 后语

文中的不足,可评论指出写文章不易,如果对你有用的话就点个赞吧~目前lua没有统一的编辑器/IDE,所以lua项目开发起来还是挺痛苦的,这里推荐使用vscode开发,搭配Lua or EmmyLua插件参考文档: ACE.JS实现一个在线代码编辑器

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。