monaco-editor代码编辑器插件源代码

效果预览

1安装 npm install monaco-editor -S
2.组件引用

属性:

  • language:默认值 javascript
  • height:默认值400
  • value/v-model
  • disabled:是否禁用
<template>
  <monaco-editor v-model="code" height="200"></monaco-editor>
</template>

<script>
import MonacoEditor from "@/components/monacoEditor/index.vue";
export default {
  components: { MonacoEditor },
  data() {
    return {
      code: "",
    };
  },

  watch: {
    code(val) {
      console.log(val);
    },
  },
};
</script>

3.组件代码:新建monacoEditor文件夹,文件夹里面分别建index.vue和index.js
index.vue代码如下

<template>
  <monaco-editor
    v-model="code"
    :read-only="disabled"
    :language="language"
    :options="options"
    :height="height"
  ></monaco-editor>
</template>

<script>
import MonacoEditor from "./index";
export default {
  components: { MonacoEditor },
  data() {
    return {
      code: "",
      options: {
        fontSize: 14,
      },
    };
  },
  props: {
    language: {
      type: String,
      default: "javascript",
    },
    disabled: Boolean,
    height: {
      type: [String, Number],
      default: 400,
    },
    value: [String, Object, Array],
  },
  watch: {
    code(val) {
      this.$emit("input", val);
    },
    value: {
      handler(val) {
        if (typeof val == "object") {
          this.code = JSON.stringify(val, null, 4);
        } else {
          this.code = val || "";
        }
      },
      immediate: true,
      deep: true,
    },
  },
};
</script>

index.js代码如下

import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution';
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution';
import 'monaco-editor/esm/vs/editor/contrib/find/findController.js';
let
  escapable = /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  keyable = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/,
  gap,
  indent,
  meta = {    // table of character substitutions
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '"': '\\"',
    "'": "\\'",
    '\\': '\\\\'
  },
  rep;

function beautifier(object, options = {}) {
  var space = options.space || 2,
    dropQuotesOnKeys = options.dropQuotesOnKeys == false ? false : true,
    dropQuotesOnNumbers = options.dropQuotesOnNumbers || false,
    inlineShortArrays = options.inlineShortArrays || false,
    inlineShortArraysDepth = options.inlineShortArraysDepth || 1,
    quoteType = options.quoteType || 'single',
    minify = options.minify || false;

  if (dropQuotesOnNumbers) walkObjectAndDropQuotesOnNumbers(object);

  var result = stringify(object, null, minify ? undefined : space, dropQuotesOnKeys, quoteType);
  if (inlineShortArrays && !minify) {
    var newResult = inlineShortArraysInResult(result);
    if (inlineShortArraysDepth > 1) {
      for (var i = 1; i < inlineShortArraysDepth; i++) {
        result = newResult;
        newResult = inlineShortArraysInResult(result);
        if (newResult == result) break;
      }
    }
    result = newResult;
  }

  return result;
}

function walkObjectAndDropQuotesOnNumbers(object) {
  if (!isObject(object)) return;
  var keys = Object.keys(object);
  if (!keys) return;

  keys.forEach(function (key) {
    var value = object[key];
    if (typeof value == 'string') {
      var number = value - 0;
      object[key] = isNaN(number) ? value : number;
    } else if (isObject(value) || Array.isArray(value)) {
      walkObjectAndDropQuotesOnNumbers(value);
    }
  });
}

function isObject(o) {
  return o && typeof o == 'object';
}

// Collapses arrays inline when they fit inside the specified width 
// in characters (including indentation).
function inlineShortArraysInResult(result, width) {
  width || (width = 80);
  if (typeof width != 'number' || width < 20) {
    throw "Invalid width '" + width + "'. Expecting number equal or larger than 20."
  }
  var list = result.split('\n'),
    i = 0,
    start = null,
    content = [];
  while (i < list.length) {
    var startMatch = !!list[i].match(/\[/),
      endMatch = !!list[i].match(/\],?/);

    if (startMatch && !endMatch) {
      content = [list[i]];
      start = i;
    } else if (endMatch && !startMatch && start) {
      content.push((list[i] || '').trim());
      var inline = content.join(' ');
      if (inline.length < width) {
        list.splice(start, i - start + 1, inline);
        i = start;
      }
      start = null;
      content = [];
    } else {
      if (start) content.push((list[i] || '').trim());
    }
    i += 1;
  }
  return list.join('\n');
}

function stringify(value, replacer, space, dropQuotesOnKeys, quoteType) {
  var i;
  gap = '';
  indent = '';
  if (typeof space === 'number') {
    for (i = 0; i < space; i += 1) {
      indent += ' ';
    }

  } else if (typeof space === 'string') {
    indent = space;
  }

  rep = replacer;
  if (replacer && typeof replacer !== 'function' &&
    (typeof replacer !== 'object' ||
      typeof replacer.length !== 'number')) {
    throw new Error('JSON.stringify');
  }
  return str('', { '': value }, dropQuotesOnKeys, quoteType);
}
function str(key, holder, dropQuotesOnKeys, quoteType) {
  var i,
    k,
    v,
    length,
    mind = gap,
    partial,
    value = holder[key];

  if (value && typeof value === 'object' &&
    typeof value.toJSON === 'function') {
    value = value.toJSON(key);
  }

  if (typeof rep === 'function') {
    value = rep.call(holder, key, value);
  }
  switch (typeof value) {
    case 'function':
      return value;
    case 'string':
      return quote(value, quoteType);
    case 'number':
      return isFinite(value) ? String(value) : 'null';
    case 'boolean':
    case 'null':
      return String(value);
    case 'object':
      if (!value) {
        return 'null';
      }
      gap += indent;
      partial = [];
      if (Object.prototype.toString.apply(value) === '[object Array]') {
        length = value.length;
        for (i = 0; i < length; i += 1) {
          partial[i] = str(i, value, dropQuotesOnKeys, quoteType) || 'null';
        }
        v = partial.length === 0
          ? '[]'
          : gap
            ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
            : '[' + partial.join(',') + ']';
        gap = mind;
        return v;
      }
      if (rep && typeof rep === 'object') {
        length = rep.length;
        for (i = 0; i < length; i += 1) {
          if (typeof rep[i] === 'string') {
            k = rep[i];
            v = str(k, value, dropQuotesOnKeys, quoteType);
            if (v) {
              partial.push((dropQuotesOnKeys ? condQuoteKey(k, quoteType) : quote(k, quoteType)) + (gap ? ': ' : ':') + v);
            }
          }
        }
      } else {
        for (k in value) {
          if (Object.prototype.hasOwnProperty.call(value, k)) {
            v = str(k, value, dropQuotesOnKeys, quoteType);
            if (v) {
              partial.push((dropQuotesOnKeys ? condQuoteKey(k, quoteType) : quote(k, quoteType)) + (gap ? ': ' : ':') + v);
            }
          }
        }
      }
      v = partial.length === 0
        ? '{}'
        : gap
          ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
          : '{' + partial.join(',') + '}';
      gap = mind;
      return v;
  }
}

function quote(string, quoteType) {
  escapable.lastIndex = 0;
  var surroundingQuote = '"';
  if (quoteType === 'single') {
    surroundingQuote = "'";
  }

  return escapable.test(string) ? surroundingQuote + string.replace(escapable, function (a) {
    var c = meta[a];
    return typeof c === 'string'
      ? c
      : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  }) + surroundingQuote : surroundingQuote + string + surroundingQuote;
}

function condQuoteKey(string, quoteType) {
  return keyable.test(string) ? string : quote(string, quoteType);
}

function noop() { }

export { monaco };

export default {
  name: 'MonacoEditor',
  props: {
    diffEditor: { type: Boolean, default: false },      //是否使用diff模式
    width: { type: [String, Number], default: '100%' },
    height: { type: [String, Number], default: '100%' },
    original: String,       //只有在diff模式下有效
    value: [String, Object],
    language: { type: String, default: 'javascript' },
    theme: { type: String, default: 'vs-dark' },
    readOnly: { type: Boolean, default: false },
    options: { type: Object, default() { return {}; } },
    editorMounted: { type: Function, default: noop },
    editorBeforeMount: { type: Function, default: noop },
    keyIndex: { type: String }
  },

  watch: {
    options: {
      deep: true,
      handler(options) {
        this.editor && this.editor.updateOptions(options);
      }
    },
    value() {
      let data = this.value
      if (this.editor && data !== this._getValue()) {
        this._setValue(data)
      }
    },

    language() {
      if (!this.editor) return;
      if (this.diffEditor) {      //diff模式下更新language
        const { original, modified } = this.editor.getModel();
        monaco.editor.setModelLanguage(original, this.language);
        monaco.editor.setModelLanguage(modified, this.language);
      } else
        monaco.editor.setModelLanguage(this.editor.getModel(), this.language);
    },

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

    style() {
      this.editor && this.$nextTick(() => {
        this.editor.layout();
      });
    }
  },

  computed: {
    style() {
      return {
        width: !/^\d+$/.test(this.width) ? this.width : `${this.width}px`,
        height: !/^\d+$/.test(this.height) ? this.height : `${this.height}px`
      }
    }
  },

  mounted() {
    this.initMonaco();
  },

  beforeDestroy() {
    this.editor && this.editor.dispose();
  },

  render() {
    return (
      <div class="monaco_editor_container" style={this.style}></div>
    );
  },

  methods: {
    initMonaco() {
      const { value, language, theme, readOnly, options } = this;
      Object.assign(options, this._editorBeforeMount());      //编辑器初始化前
      this.editor = monaco.editor[this.diffEditor ? 'createDiffEditor' : 'create'](this.$el, {
        value: (typeof value == 'string') ? value : beautifier(value),
        language: language,
        theme: theme,
        readOnly: readOnly,
        ...options
      });
      this.diffEditor && this._setModel(this.value, this.original);
      this._editorMounted(this.editor);      //编辑器初始化后
    },

    _getEditor() {
      if (!this.editor) return null;
      return this.diffEditor ? this.editor.modifiedEditor : this.editor;
    },

    _setModel(value, original) {     //diff模式下设置model
      const { language } = this;
      const originalModel = monaco.editor.createModel(original, language);
      const modifiedModel = monaco.editor.createModel(value, language);
      this.editor.setModel({
        original: originalModel,
        modified: modifiedModel
      });
    },

    _setValue(value) {
      let editor = this._getEditor();
      if (editor) return editor.setValue(value);
    },

    _getValue() {
      let editor = this._getEditor();
      if (!editor) return '';
      return editor.getValue();
    },

    _editorBeforeMount() {
      const options = this.editorBeforeMount(monaco);
      return options || {};
    },

    _editorMounted(editor) {
      this.editorMounted(editor, monaco);
      if (this.diffEditor) {
        editor.onDidUpdateDiff((event) => {
          const value = this._getValue();
          this._emitChange(value, event);
        });
      } else {
        editor.onDidChangeModelContent(event => {
          const value = this._getValue();
          this._emitChange(value, event);
        });
      }
    },

    _emitChange(value, event) {
      this.$emit('change', value, event);
      this.$emit('input', value);
    }
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容