溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么使用Monaco?Editor開(kāi)發(fā)SQL代碼提示編輯器

發(fā)布時(shí)間:2022-08-08 11:44:32 來(lái)源:億速云 閱讀:876 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了怎么使用Monaco Editor開(kāi)發(fā)SQL代碼提示編輯器的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇怎么使用Monaco Editor開(kāi)發(fā)SQL代碼提示編輯器文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

    安裝

    安裝依賴(lài),這里請(qǐng)注意版本

    yarn add monaco-editor@0.29.1
    yarn add monaco-editor-webpack-plugin@5.0.0

    配置 webpack 插件

    // vue.config.js
    ...
    const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
    module.export = {
      ...
      configureWebpack: {
        name: name,
        resolve: {
          alias: {
            '@': resolve('src'),
          },
        },
        plugins: [new MonacoWebpackPlugin()],
      },
      ...
    }

    請(qǐng)注意 monaco-editor-webpack-plugin 和 monaco-editor 的對(duì)應(yīng)關(guān)系,否則可能會(huì)出現(xiàn)無(wú)法運(yùn)行的情況。

    monaco-editor-webpack-pluginmonaco-editor
    7.*.*>= 0.31.0
    6.*.*0.30.*
    5.*.*0.29.*
    4.*.*0.25.*, 0.26.*, 0.27.*, 0.28.*
    3.*.*0.22.*, 0.23.*, 0.24.*
    2.*.*0.21.*
    1.9.*0.20.*
    1.8.*0.19.*
    1.7.*0.18.*

    簡(jiǎn)易 SQL 編輯器

    先上干貨!

    <template>
      <div ref="codeContainer" class="editor-container" : />
    </template>
    <script>
    import * as monaco from 'monaco-editor'
    /**
     * VS Code 編輯器
     *
     * 通過(guò) getEditorVal 函數(shù)向外傳遞編輯器即時(shí)內(nèi)容
     * 通過(guò) initValue 用于初始化編輯器內(nèi)容。
     * 編輯器默認(rèn) sql 語(yǔ)言,支持的語(yǔ)言請(qǐng)參考 node_modules\monaco-editor\esm\vs\basic-languages 目錄下~
     * 編輯器樣式僅有   'vs', 'vs-dark', 'hc-black' 三種
     */
    export default {
      name: 'MonacoEditor',
      props: {
        initValue: {
          type: String,
          default: '',
        },
        readOnly: Boolean,
        language: {
          type: String,
          default: 'sql',
        },
        height: {
          type: Number,
          default: 300,
        },
        theme: {
          type: String,
          default: 'vs',
        },
      },
      data() {
        return {
          monacoEditor: null, // 語(yǔ)言編輯器
        }
      },
      computed: {
        inputVal() {
          return this.monacoEditor?.getValue()
        },
      },
      watch: {
        inputVal() {
          if (this.monacoEditor) {
            this.$emit('change', this.monacoEditor.getValue())
          }
        },
        theme() {
          this.setTheme(this.theme)
        },
        height() {
          this.layout()
        },
      },
      mounted() {
        this.initEditor()
      },
      beforeDestroy() {
        if (this.monacoEditor) {
          this.monacoEditor.dispose()
        }
      },
      methods: {
        initEditor() {
          if (this.$refs.codeContainer) {
            this.registerCompletion()
            // 初始化編輯器,確保dom已經(jīng)渲染
            this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, {
              value: '', // 編輯器初始顯示文字
              language: 'sql', // 語(yǔ)言
              readOnly: this.readOnly, // 是否只讀 Defaults to false | true
              automaticLayout: true, // 自動(dòng)布局
              theme: this.theme, // 官方自帶三種主題vs, hc-black, or vs-dark
              minimap: {
                // 關(guān)閉小地圖
                enabled: false,
              },
              tabSize: 2, // tab縮進(jìn)長(zhǎng)度
            })
          }
          this.setInitValue()
        },
        focus() {
          this.monacoEditor.focus()
        },
        layout() {
          this.monacoEditor.layout()
        },
        getValue() {
          return this.monacoEditor.getValue()
        },
        // 將 initValue Property 同步到編輯器中
        setInitValue() {
          this.monacoEditor.setValue(this.initValue)
        },
        setTheme() {
          monaco.editor.setTheme(this.theme)
        },
        getSelectionVal() {
          const selection = this.monacoEditor.getSelection() // 獲取光標(biāo)選中的值
          const { startLineNumber, endLineNumber, startColumn, endColumn } = selection
          const model = this.monacoEditor.getModel()
          return model.getValueInRange({
            startLineNumber,
            startColumn,
            endLineNumber,
            endColumn,
          })
        },
        setPosition(column, lineNumber) {
          this.monacoEditor.setPosition({ column, lineNumber })
        },
        getPosition() {
          return this.monacoEditor.getPosition()
        },
      },
    }
    </script>
    <style lang="scss" scoped></style>

    相關(guān)功能

    獲取選中代碼

        getSelectionVal() {
          const selection = this.monacoEditor.getSelection() // 獲取光標(biāo)選中的值
          const { startLineNumber, endLineNumber, startColumn, endColumn } = selection
          const model = this.monacoEditor.getModel()
          return model.getValueInRange({
            startLineNumber,
            startColumn,
            endLineNumber,
            endColumn,
          })
        },

    替換選中代碼

    insertStringInTemplate(str) {
          const selection = this.monacoEditor.getSelection() // 獲取光標(biāo)選中的值
          const { startLineNumber, endLineNumber, startColumn, endColumn } = selection
          const model = this.monacoEditor.getModel()
          const textBeforeSelection = model.getValueInRange({
            startLineNumber: 1,
            startColumn: 0,
            endLineNumber: startLineNumber,
            endColumn: startColumn,
          })
          const textAfterSelection = model.getValueInRange({
            startLineNumber: endLineNumber,
            startColumn: endColumn,
            endLineNumber: model.getLineCount(),
            endColumn: model.getLineMaxColumn(model.getLineCount()),
          })
          this.monacoEditor.setValue(textBeforeSelection + str + textAfterSelection)
          this.monacoEditor.focus()
          this.monacoEditor.setPosition({
            lineNumber: startLineNumber,
            column: startColumn + str.length,
          })
        },

    處理光標(biāo)位置

      setPosition(column, lineNumber) {
          this.monacoEditor.setPosition({ column, lineNumber })
        },
        getPosition() {
          return this.monacoEditor.getPosition()
        },

    自定義 SQL 庫(kù)表提示,并保留原有 SQL 提示

    首先由后端提供具體的庫(kù)表信息:

    export const hintData = {
      adbs: ['dim_realtime_recharge_paycfg_range', 'dim_realtime_recharge_range'],
      dimi: ['ads_adid', 'ads_spec_adid_category'],
    }

    然后根據(jù)已有庫(kù)表信息進(jìn)行自定義 AutoComplete

    import * as monaco from 'monaco-editor'
    import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'
    const { keywords } = language
    export default {
      ...
      mounted() {
        this.initEditor()
      },
      methods: {
        ...
        registerCompletion() {
          const _that = this
          monaco.languages.registerCompletionItemProvider('sql', {
            triggerCharacters: ['.', ...keywords],
            provideCompletionItems: (model, position) => {
              let suggestions = []
              const { lineNumber, column } = position
              const textBeforePointer = model.getValueInRange({
                startLineNumber: lineNumber,
                startColumn: 0,
                endLineNumber: lineNumber,
                endColumn: column,
              })
              const tokens = textBeforePointer.trim().split(/\s+/)
              const lastToken = tokens[tokens.length - 1] // 獲取最后一段非空字符串
              if (lastToken.endsWith('.')) {
                const tokenNoDot = lastToken.slice(0, lastToken.length - 1)
                if (Object.keys(_that.hintData).includes(tokenNoDot)) {
                  suggestions = [..._that.getTableSuggest(tokenNoDot)]
                }
              } else if (lastToken === '.') {
                suggestions = []
              } else {
                suggestions = [..._that.getDBSuggest(), ..._that.getSQLSuggest()]
              }
              return {
                suggestions,
              }
            },
          })
        },
        // 獲取 SQL 語(yǔ)法提示
        getSQLSuggest() {
          return keywords.map((key) => ({
            label: key,
            kind: monaco.languages.CompletionItemKind.Enum,
            insertText: key,
          }))
        },
        getDBSuggest() {
          return Object.keys(this.hintData).map((key) => ({
            label: key,
            kind: monaco.languages.CompletionItemKind.Constant,
            insertText: key,
          }))
        },
        getTableSuggest(dbName) {
          const tableNames = this.hintData[dbName]
          if (!tableNames) {
            return []
          }
          return tableNames.map((name) => ({
            label: name,
            kind: monaco.languages.CompletionItemKind.Constant,
            insertText: name,
          }))
        },
        initEditor() {
          if (this.$refs.codeContainer) {
            this.registerCompletion()
            // 初始化編輯器,確保dom已經(jīng)渲染
            this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, {
              value: '', // 編輯器初始顯示文字
              language: 'sql', // 語(yǔ)言
              readOnly: this.readOnly, // 是否只讀 Defaults to false | true
              automaticLayout: true, // 自動(dòng)布局
              theme: this.theme, // 官方自帶三種主題vs, hc-black, or vs-dark
              minimap: {
                // 關(guān)閉小地圖
                enabled: false,
              },
              tabSize: 2, // tab縮進(jìn)長(zhǎng)度
            })
          }
          this.setValue(this.value)
        },
      }
    }

    編輯器 resize

        resize() {
          this.monacoEditor.layout()
        },

    編輯器設(shè)置主題

    注意!設(shè)置主題并非在編輯器實(shí)例上修改的哦!

        setTheme() {
          monaco.editor.setTheme(this.theme)
        },

    SQL 代碼格式化

    編輯器自身不支持 sql 格式化(試了下 JavaScript 是支持的),所以用到了 sql-formatter 這個(gè)庫(kù)。

    import { format } from 'sql-formatter'
    ...
        format() {
          this.monacoEditor.setValue(
            format(this.monacoEditor.getValue(), {
              indentStyle: 'tabularLeft',
            }),
          )
        },
    ...

    右鍵菜單漢化

    需要安裝以下兩個(gè)庫(kù)

    npm install monaco-editor-nls --save
    npm install monaco-editor-esm-webpack-plugin --save-dev

    記得銷(xiāo)毀編輯器對(duì)象哦

      beforeDestroy() {
        if (this.monacoEditor) {
          this.monacoEditor.dispose()
        }
      },

    踩坑

    下面是我遇到的幾個(gè)坑。

    • 最新版本的 Monaco Editor 已經(jīng)使用了 ES2022 的語(yǔ)法,所以老項(xiàng)目可能會(huì)出現(xiàn)編譯不過(guò)的問(wèn)題。所以我把版本調(diào)低了一些。

    • 在最初調(diào)試編輯器的時(shí)候出現(xiàn)了無(wú)法編輯的情況,后來(lái)發(fā)現(xiàn)是同事用到了 default-passive-events 這個(gè)庫(kù)來(lái)關(guān)閉 chrome 的 Added non-passive event listener to a scroll-blocking <some> event. Consider marking event handler as 'passive' to make the page more responsive 警告。結(jié)果攔截一些 event。

    如何快速去看懂 Monaco Editor

    一開(kāi)始我看它的官方文檔是非常懵的,各種接口、函數(shù)、對(duì)象的定義,完全不像是個(gè)前端庫(kù)那么好理解。鼓搗了好久才慢慢找到門(mén)路。

    • 先看示例

      • 查看它的 playground,上面其實(shí)是有一些功能可以直接找到的。

      • 查看它在 github 上的 /samples 目錄,里面也有不少示例。

      • 去掘金這類(lèi)網(wǎng)站上找別人寫(xiě)的示例,能有不少啟發(fā)。

    • 再看 API

      • 了解了自己所需要的功能相關(guān)的代碼,再去看它文檔的 API 就會(huì)發(fā)現(xiàn)容易理解多了。逐步發(fā)散理解更多關(guān)聯(lián)功能。

    關(guān)于“怎么使用Monaco Editor開(kāi)發(fā)SQL代碼提示編輯器”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“怎么使用Monaco Editor開(kāi)發(fā)SQL代碼提示編輯器”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI