Vue(27)vue-codemirror實現線上程式碼編譯器

Silent丿丶黑羽發表於2021-12-30

前言

如果我們想在Web端實現線上程式碼編譯的效果,那麼需要使用元件vue-codemirror,他是將CodeMirror進行了再次封裝

  • 支援程式碼高亮
  • 62種主題顏色,例如monokai等等
  • 支援json, sql, javascript,css,xml, html,yaml, markdown, python編輯模式,預設為 json
  • 支援快速搜尋
  • 支援自動補全提示
  • 支援自動匹配括號
     

環境準備

npm install jshint
npm install jsonlint
npm install script-loader
npm install vue-codemirror

 

封裝元件

我們可以在專案中的components中將vue-codemirror進行再次封裝

<template>
  <codemirror
    ref="myCm"
    v-model="editorValue"
    :options="cmOptions"
    @changes="onCmCodeChanges"
    @blur="onCmBlur"
    @keydown.native="onKeyDown"
    @mousedown.native="onMouseDown"
    @paste.native="OnPaste"
  >
  </codemirror>
</template>

<script>
import { codemirror } from "vue-codemirror";
import 'codemirror/keymap/sublime'
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/xml/xml.js";
import "codemirror/mode/htmlmixed/htmlmixed.js";
import "codemirror/mode/css/css.js";
import "codemirror/mode/yaml/yaml.js";
import "codemirror/mode/sql/sql.js";
import "codemirror/mode/python/python.js";
import "codemirror/mode/markdown/markdown.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/hint/xml-hint.js";
import "codemirror/addon/hint/css-hint.js";
import "codemirror/addon/hint/html-hint.js";
import "codemirror/addon/hint/sql-hint.js";
import "codemirror/addon/hint/anyword-hint.js";
import "codemirror/addon/lint/lint.css";
import "codemirror/addon/lint/lint.js";
import "codemirror/addon/lint/json-lint";
import 'codemirror/addon/selection/active-line'
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/anyword-hint.js";
require("script-loader!jsonlint");
import "codemirror/addon/lint/javascript-lint.js";
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/xml-fold.js";
import "codemirror/addon/fold/comment-fold.js";
import "codemirror/addon/fold/markdown-fold.js";
import "codemirror/addon/fold/indent-fold.js";
import "codemirror/addon/edit/closebrackets.js";
import "codemirror/addon/edit/closetag.js";
import "codemirror/addon/edit/matchtags.js";
import "codemirror/addon/edit/matchbrackets.js";
import "codemirror/addon/selection/active-line.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";
import "codemirror/addon/display/autorefresh.js";
import "codemirror/addon/selection/mark-selection.js";
import "codemirror/addon/search/match-highlighter.js";
export default {
  name: "index",
  components: {codemirror},
  props: ["cmTheme", "cmMode", "cmIndentUnit", "autoFormatJson"],
  data() {
    return {
      editorValue: '{}',
      cmOptions: {
        theme: !this.cmTheme || this.cmTheme === "default" ? "default" : this.cmTheme,  // 主題
        mode: !this.cmMode || this.cmMode === "default" ? "application/json" : this.cmMode,  // 程式碼格式
        tabSize: 4,  // tab的空格個數
        indentUnit: !this.cmIndentUnit ? 2 : this.cmIndentUnit,  // 一個塊(編輯語言中的含義)應縮排多少個空格
        autocorrect: true,  // 自動更正
        spellcheck: true,  // 拼寫檢查
        lint: true,  // 檢查格式
        lineNumbers: true,  //是否顯示行數
        lineWrapping: true, //是否自動換行
        styleActiveLine: true,  //line選擇是是否高亮
        keyMap: 'sublime',  // sublime編輯器效果
        matchBrackets: true,  //括號匹配
        autoCloseBrackets: true,  // 在鍵入時將自動關閉括號和引號
        matchTags: { bothTags: true },  // 將突出顯示游標周圍的標籤
        foldGutter: true,  // 可將物件摺疊,與下面的gutters一起使用
        gutters: [
          "CodeMirror-lint-markers",
          "CodeMirror-linenumbers",
          "CodeMirror-foldgutter"
        ],
        highlightSelectionMatches: {
          minChars: 2,
          style: "matchhighlight",
          showToken: true
        },
      },
      enableAutoFormatJson: this.autoFormatJson == null ? true : this.autoFormatJson,  // json編輯模式下,輸入框失去焦點時是否自動格式化,true 開啟, false 關閉
    }
  },
  created() {
    try {
      if (!this.editorValue) {
        this.cmOptions.lint = false;
        return;
      }
      if (this.cmOptions.mode === "application/json") {
        if (!this.enableAutoFormatJson) {
          return;
        }
        this.editorValue = this.formatStrInJson(this.editorValue);
      }
    } catch (e) {
      console.log("初始化codemirror出錯:" + e);
    }
  },
  methods: {
    resetLint() {
      if (!this.$refs.myCm.codemirror.getValue()) {
        this.$nextTick(() => {
          this.$refs.myCm.codemirror.setOption("lint", false);
        });
        return;
      }
      this.$refs.myCm.codemirror.setOption("lint", false);
      this.$nextTick(() => {
        this.$refs.myCm.codemirror.setOption("lint", true);
      });
    },
    // 格式化字串為json格式字串
    formatStrInJson(strValue) {
      return JSON.stringify(
        JSON.parse(strValue),
        null,
        this.cmIndentUnit
      );
    },
    onCmCodeChanges(cm, changes) {
      this.editorValue = cm.getValue();
      this.resetLint();
    },
    // 失去焦點時處理函式
    onCmBlur(cm, event) {
      try {
        let editorValue = cm.getValue();
        if (this.cmOptions.mode === "application/json" && editorValue) {
          if (!this.enableAutoFormatJson) {
            return;
          }
          this.editorValue = this.formatStrInJson(editorValue);
        }
      } catch (e) {
        // 啥也不做
      }
    },
    // 按下鍵盤事件處理函式
    onKeyDown(event) {
      const keyCode = event.keyCode || event.which || event.charCode;
      const keyCombination =
          event.ctrlKey || event.altKey || event.metaKey;
      if (!keyCombination && keyCode > 64 && keyCode < 123) {
        this.$refs.myCm.codemirror.showHint({ completeSingle: false });
      }
    },
    // 按下滑鼠時事件處理函式
    onMouseDown(event) {
      this.$refs.myCm.codemirror.closeHint();
    },
    // 黏貼事件處理函式
    OnPaste(event) {
      if (this.cmOptions.mode === "application/json") {
        try {
          this.editorValue = this.formatStrInJson(this.editorValue);
        } catch (e) {
          // 啥都不做
        }
      }
    },
  }
}
</script>

<style scoped>

</style>

此元件預設配置了json編譯器,cmOptions中是程式碼編譯器的配置項,需要額外的功能也可以去看官方文件配置
接下來看展示效果

可以看到我們輸入了json格式的字串,即使格式不正確,會給我們錯誤提示,並且也會給我們自動格式化
 

python編譯器

我們封裝的元件預設是json編譯器,如果我們想使用其他語言,也很簡單,只需要匯入其他語言的mode

<template>
  <div>
    <el-button type="primary" icon="el-icon-circle-check-outline" @click="handleConfirm" round>
      點選儲存
    </el-button>
    <el-button icon="el-icon-caret-right" type="info" @click="handleRunCode" round>
      線上執行
    </el-button>
  <code-editor
    :cmTheme="cmTheme"
    :cmMode="cmMode"
  >
  </code-editor>
  </div>
</template>

<script>
import codeEditor from '@/components/CodeEditor/index'
import 'codemirror/theme/monokai.css'  // 匯入monokai主題
import 'codemirror/mode/python/python.js'  // 匯入python
export default {
  name: "index",
  components: {
    codeEditor
  },
  data() {
    return {
      cmTheme: "monokai",
      cmMode: "python",
    }
  },
  methods: {
    handleConfirm() {},
    handleRunCode() {}
  }
}
</script>

<style>
.CodeMirror {
  position: relative;
  height: 100vh;
  overflow: hidden;
  margin-top: 10px;
}
</style>
<style lang="scss" scoped>
</style>

效果如下

相關文章