codemirrorのlint error/warningをモバイルでも表示する



CodeMirrorのlintによるエラーとワーニング表示があります

![スクリーンショット 2018-03-24 19.12.17.png](https://qiita-image-store.s3.amazonaws.com/0/227015/04c72559-58f6-852c-b5cb-e6dd6ec5314e.png)

これを使っていたのですが，マウスオーバーでtooltipが表示されるので，マウスのないスマホではエラーがあるのはわかるけど，何がエラーかわからない　という状況になりました．

それでは困るので，マークをタップ（というか行番号付近をタップ）した時に，tooltipを表示するようにしてみました．

適当な部分をタップすると消えます．

##仕組み
CodeMirrorのtouchendイベントで，マークをタップしたらそこに疑似マウスオーバーイベントを発生させることでtooltipを出す　ということをしています．

tooltipをきちんと自分で生成して表示するところまでやれば，疑似マウスオーバーイベントは不要なのですが，
どうもこのtooltipの機能はcodemirrorと密接になっているようで，
その部分だけを取り出すことができなかったため，疑似イベントで対応しています．
（もちろんコピペしたらできますが，同じコードが複数箇所にあるのは好きではないので．．．．）

touchendイベント自体がスマホじゃないと発生しないので，スマホのときだけ動作します．
PCの場合は通常通りマウスオーバーで表示されます．

##コード

```javascript

  CodeMirror.on(cm.display.scroller, "touchend", function (e) {
    let touch = cm.display.activeTouch;
    let date = new Date;
    if (touch &&  touch.left != null &&
        !touch.moved && date - touch.start < 300) {

      //エディタの何処かをsingle tapした
      if (!touch.prev) {

        //行に変換するならこれ
        var pos = cm.coordsChar(touch, "page");

        //gutterの範囲判定
        if(touch.left < Math.floor(cm.display.gutters.getBoundingClientRect().right)){
          showLintTooltip(pos.line);
          return;
        }
      }
    }

    //hide tooltip
    if(currentTooptipTarget) {
      dispatchMouseEvent(currentTooptipTarget, "mouseout", 0, 0);
      currentTooptipTarget = null;
    }
  });


  let currentTooptipTarget = null ;
  function showLintTooltip(line){

    let markers = jsEditor.state.lint.marked;
    let info = jsEditor.lineInfo(line);
    for (let marker of markers) {
      if (marker.lines[0].lineNo() === line) {
        //need to tooltip
        let element = info.gutterMarkers["CodeMirror-lint-markers"];
        if (element) {
          let rect = element.getBoundingClientRect();
          console.log(rect);

          if(currentTooptipTarget == element){
            //hide tooltip
            dispatchMouseEvent(element, "mouseout", rect.x, rect.y);
            currentTooptipTarget = null;
          }else {
            //hide tooltip
            if(currentTooptipTarget) {
              dispatchMouseEvent(currentTooptipTarget, "mouseout", rect.x, rect.y);
            }
            //show tooltip
            dispatchMouseEvent(element, "mouseover", rect.left+rect.width/2, rect.top+rect.height/2);

            currentTooptipTarget = element;
          }
          return;
        }
      }
    }
  }

  function dispatchMouseEvent(elm, type,x,y){

    var mouseMoveEvent = document.createEvent("MouseEvents");

    mouseMoveEvent.initMouseEvent(
        type, //event type : click, mousedown, mouseup, mouseover, mousemove, mouseout.
        true, //canBubble
        false, //cancelable
        window, //event's AbstractView : should be window
        1, // detail : Event's mouse click count
        x, // screenX
        y, // screenY
        x, // clientX
        y, // clientY
        false, // ctrlKey
        false, // altKey
        false, // shiftKey
        false, // metaKey
        0, // button : 0 = click, 1 = middle button, 2 = right button
        null // relatedTarget : Only used with some event types (e.g. mouseover and mouseout). In other cases, pass null.
    );

    elm.dispatchEvent(mouseMoveEvent);
  }

```


##注意点
上のコードで表示はできるようになったものの，iOS(safari)のcssにバグ（？）があるようで，`position:fixed`での位置指定が上手く行かないようでした．（縦方向にずれる）
その場合はcssで`position:absolute`でゴニョゴニョしてあげるとちょうどよくなると思います．

なお，Androidでは（自分の試した限り）問題なく動いています．

##その他
iosでcodemirrorを使う場合，日本語入力が変になる問題があるので，こっちの対応も必須になると思います（参考にさせていただきました）
https://qiita.com/ttakuru88/items/e363267772cacf4c1fa2




そもそもスマホでコードを書きたいって需要はどのくらいあるんだろう・・・？