CodeMirrorでテキストフィールドを拡張する

俺の考えた最強のTODOリストを作るために便利なテキストフィールドを作ってみます。

![screencast 2018-03-16 22-34-36.gif](https://qiita-image-store.s3.amazonaws.com/0/24124/d2af0a59-af91-8d3a-1719-626355480e72.gif)

<p data-height="265" data-theme-id="0" data-slug-hash="MVyzzx" data-default-tab="js,result" data-user="mottox2" data-embed-version="2" data-pen-title="Custom CodeMirror TextField" class="codepen">See the Pen <a href="https://codepen.io/mottox2/pen/MVyzzx/">Custom CodeMirror TextField</a> by Yuki Takemoto (<a href="https://codepen.io/mottox2">@mottox2</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## なぜCodeMirrorか

TextFieldにハイライトをつけるには、まずはcontenteditableを使った、WYSIWYGエディタによく使われる方法が考えられますが、これを生で触るのは非常に大変です。なので便利なライブラリに頼ります。
調べたところCodeMirrorがいいのではと感じたのでCodeMirrorを使っていきます。[^1]

CodeMirrorは、Web上で動いて、JSなどのコードにハイライトをつけることの出来るライブラリです。
各言語のハイライトはmodeという形で管理されていて、自分でmodeを追加することができます。つまり、独自のハイライトを定義することが出来ます。

今回はこのCodeMirrorに独自のハイライトmodeを定義し、TextAreaをTextFieldライクな表示にすることで、便利テキストフィールドを作っていきます。

## 環境
CodeMirror v5.35.0
**[CSS]**
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/codemirror.css
**[JavaScript]**
https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/codemirror.js
https://codemirror.net/mode/javascript/javascript.js

## 1. CodeMirrorを導入してみる

普通にうごきます。このようにCodeMirrorはmodeを指定することでシンタックスハイライトをつけられる便利なライブラリです。

<p data-height="265" data-theme-id="0" data-slug-hash="wmGopW" data-default-tab="html,result" data-user="mottox2" data-embed-version="2" data-pen-title="Simple CodeMirror sample" class="codepen">See the Pen <a href="https://codepen.io/mottox2/pen/wmGopW/">Simple CodeMirror sample</a> by Yuki Takemoto (<a href="https://codepen.io/mottox2">@mottox2</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

[CodeMirrorでブラウザ上で動作するコードエディタを作る](https://qiita.com/qmotas/items/90dbe1f8f60136cf39bf)を見るとわかりやすいと思います。

## 2. mode(ハイライト方法)を定義する
1.の例では元から用意されていたJavaScript modeを選択しましたが、今回は自分でハイライトのルールを作りたいのでmodeを定義します。
[公式のDoc](https://codemirror.net/doc/manual.html#modeapi) に解説がありますが、非常に読むのがしんどいので、実際に動作しているmodeのコードを読みます。

**例**
[yaml mode](https://github.com/codemirror/CodeMirror/blob/60f395169a8800ed739001f47cf6a2a1aaa22d40/mode/yaml/yaml.js)
[js mode](https://github.com/codemirror/CodeMirror/blob/60f395169a8800ed739001f47cf6a2a1aaa22d40/mode/javascript/javascript.js)


このようにGithubのリポジトリに各言語で定義されているmodeがあるので読んでいくと以下の書き方で書けることがわかります。

```js:modeの定義
CodeMirror.defineMode('MODE_NAME', {
  token: function(stream, state) {
    if (stream.match(/REGEXP/)) {
      return 'keyword'
    }
    return null
  }
})
```
streamで処理していく形です。マッチしたら keyword, commentなどのstringで返すとそれに応じたclassを付与してくれます。
ドキュメントに書いてくれればいいんですけど、defineModeに関する記述は見つかりませんでした。[^2]

このmodeを定義して、動かしてみるとこんな感じに動かせます。

<p data-height="265" data-theme-id="0" data-slug-hash="yKOQzR" data-default-tab="js,result" data-user="mottox2" data-embed-version="2" data-pen-title="Custom CodeMirror Sample" class="codepen">See the Pen <a href="https://codepen.io/mottox2/pen/yKOQzR/">Custom CodeMirror Sample</a> by Yuki Takemoto (<a href="https://codepen.io/mottox2">@mottox2</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
## 3. TextAreaをTextFieldにします。
改行禁止にすれば同じようなものが再現できます。ここで本来注意しなければいけないのは、キーボードの挙動だけでなくペースト時の挙動も把握しておく必要があります。
しかし、CodeMirrorは便利なもので毎回の変更をbeforeChangeイベントの引数で扱うことができます。
```js
beforeChange = function(editor, change, value ) {
  // Do something...
}
```

```js
{from: Pos, to: Pos, text: Array(1), removed: Array(1), origin: "paste"}
{from: Pos, to: Pos, text: Array(1), removed: Array(1), origin: "input"}
```
これらの情報を持ったオブジェクトが渡ってくるので、Changeオブジェクトをupdateすることで入力したものを上書きすることが出来ます。

参考
https://stackoverflow.com/questions/13026285/codemirror-for-just-one-line-textfield

<p data-height="265" data-theme-id="0" data-slug-hash="MVyzzx" data-default-tab="js,result" data-user="mottox2" data-embed-version="2" data-pen-title="Custom CodeMirror TextField" class="codepen">See the Pen <a href="https://codepen.io/mottox2/pen/MVyzzx/">Custom CodeMirror TextField</a> by Yuki Takemoto (<a href="https://codepen.io/mottox2">@mottox2</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

[^1]: kibe.la のテキストエリアはCodeMirrorを使ってMarkdownにハイライトをつけていました。これがなかなかいい挙動で初めて触った時感動しました。
[^2]: defineSinpleModeというメソッドも定義されていますが、npmで配布されているCodeMirrorではexportされていません。

## やっていき宣言

これからこのテキストフィールドを使って俺の考えた最強のTODOリストを作っていきます。

なおReactからCodeMirrorを使うには react-codemirror2 がよさそうな感じです。