CodeMirrorをTypescript+Vue.jsで使う方法

# 概要
CodemirrorはJavaScript製の多言語に対応したテキストエディタです。
今回は、このCodeMirrorをTypescript+Vue.jsな環境で使えるように実装してみました。

# 準備
```bash
vue create vue-codemirror-sample
cd vue-codemirror-sample
```
vueのバージョンは2.x
必要な機能は `TypeScript`、後はお好みで
スタイルはVue.extend()で書きます。 class style派は置き換えて読んでください。

## ライブラリのインストール
```bash
npm install codemirror
npm install @types/codemirror
```

# 実装

## CodeMirrorを読み込む

まずは、`App.vue`からいらないコードを消してスッキリさせます。

```diff_html:App.vue
<template>
  <div id="app">
-   <img alt="Vue logo" src="./assets/logo.png" />
-   <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
-import HelloWorld from './components/HelloWorld.vue'

export default Vue.extend({
  name: 'App',
-  components: {
-    HelloWorld,
-  },
})
</script>

-<style>
-#app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-  margin-top: 60px;
-}
-</style>

```

次にcodemirrorを挿入します。
見ずらいので追加でmonokaiテーマも追加します。

```diff_html:App.vue
<template>
  <div id="app">
+    <textarea id="editor"></textarea>
  </div>
</template>

<script lang="ts">
+import CodeMirror, { EditorConfiguration, Editor } from 'codemirror'
+import 'codemirror/lib/codemirror.css'
+import 'codemirror/theme/monokai.css'
import Vue from 'vue'

+type DataType = {
+  editor?: Editor
+}
export default Vue.extend({
  name: 'App',
+  data(): DataType {
+    return {}
+  },
+  // mountedでCodemirrorを生成する
+  mounted(): void {
+    const editor: HTMLTextAreaElement | null = document.querySelector('#editor')
+    if (editor == null) {
+      throw new Error('codemirrorを挿入するtextariaがありません')
+    }
+    const config: EditorConfiguration = {
+      theme: 'monokai',
+      lineNumbers: true,
+    }
+    this.editor = CodeMirror.fromTextArea(editor, config)
+  },
})
</script>
```

これでとりあえず読み込めるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283899/fc17dc9f-393d-3306-850a-d10d31144766.png)

## アドオンの追加方法

アドオンの追加は、importするだけです。

```ts:
import 'codemirror/addon/hint/show-hint.js'
```

jsで自動補完とシンタックスハイライトを使えるようにしてみます。

```diff_html:App.vue
<template>
  <div id="app">
    <textarea id="editor"></textarea>
  </div>
</template>

<script lang="ts">
import CodeMirror, { EditorConfiguration, Editor } from 'codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/monokai.css'
+import 'codemirror/addon/hint/show-hint.js'
+import 'codemirror/addon/hint/show-hint.css'
+import 'codemirror/mode/javascript/javascript.js'
+import 'codemirror/addon/hint/javascript-hint.js'
import Vue from 'vue'

type DataType = {
  editor?: Editor
}
export default Vue.extend({
  name: 'App',
  data(): DataType {
    return {}
  },
  mounted(): void {
    // mountedでCodemirrorを生成する
    const editor: HTMLTextAreaElement | null = document.querySelector('#editor')
    if (editor == null) {
      throw new Error('codemirrorを挿入するtextariaがありません')
    }
    const config: EditorConfiguration = {
      theme: 'monokai',
+      mode: 'javascript',
      lineNumbers: true,
+      showHint: true,
+      extraKeys: { 'Ctrl-Space': 'autocomplete' },
    }
    this.editor = CodeMirror.fromTextArea(editor, config)
  },
})
</script>
```

これで、ctrl+spaceキーでjsの自動補完とシンタックスハイライトが使えるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283899/05e94fb4-700a-aa37-7d7c-d7d2e40c08dd.png)

おわり

# 完成したApp.vue
```vue:App.vue
<template>
  <div id="app">
    <textarea id="editor"></textarea>
  </div>
</template>

<script lang="ts">
import CodeMirror, { EditorConfiguration, Editor } from 'codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/monokai.css'
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/addon/hint/javascript-hint.js'
import Vue from 'vue'

type DataType = {
  editor?: Editor
}
export default Vue.extend({
  name: 'App',
  data(): DataType {
    return {}
  },
  mounted(): void {
    // mountedでCodemirrorを生成する
    const editor: HTMLTextAreaElement | null = document.querySelector('#editor')
    if (editor == null) {
      throw new Error('codemirrorを挿入するtextariaがありません')
    }
    const config: EditorConfiguration = {
      theme: 'monokai',
      mode: 'javascript',
      lineNumbers: true,
      showHint: true,
      extraKeys: { 'Ctrl-Space': 'autocomplete' },
    }
    this.editor = CodeMirror.fromTextArea(editor, config)
  },
})
</script>
```