LoginSignup
68

More than 5 years have passed since last update.

CodeMirrorでAutoCompleteする

Last updated at Posted at 2014-11-14

JavaScriptで作られたエディタCodeMirrorで、こんな風に補完をしたい。

準備

まず以下のjsとcssを読み込む

  • codemirror/lib/codemirror.js
  • codemirror/lib/codemirror.css
  • codemirror/addon/hint/show-hint.js
  • codemirror/addon/hint/show-hint.css

同期的に補完候補を表示する

以下coffeescript
elはなんか適当なDOMTextAreaELement渡してください。

{Pos} = CodeMirror
autocomplete = (cm) ->
  CodeMirror.showHint cm, ->
    cur   = cm.getCursor()
    token = cm.getTokenAt(cur)
    start = token.start
    end   = token.end
    from  = Pos(cur.line, start)
    to    = Pos(cur.line, end)

    list:[
      "aaa"
      "bbb"
      "ccc"
      ]
    from: from
    to: to

CodeMirror.fromTextArea el,
  extraKeys:
   'Tab': autocomplete

TabキーでAutoCompleteを発火させる。showHintで上のような構造体を渡せば補完が走る

非同期的に補完候補を表示する

{Pos} = CodeMirror
autocompleteAsync = (cm) ->
  callback = (cm) ->
    cur   = cm.getCursor()
    token = cm.getTokenAt(cur)
    start = token.start
    end   = token.end
    from  = Pos(cur.line, start)
    to    = Pos(cur.line, end)

    setTimeout ->
      CodeMirror.showHint cm, ->
        list:[
          "aaa"
          "bbb"
          "ccc"
          ]
        from: from
        to: to
    , 400

  callback.async = true
  CodeMirror.showHint cm, callback

CodeMirror.fromTextArea el,
  extraKeys:]
   'Tab': autocompleteAsync

入力したら常に補完できないか確認する

Tabを押さずにキー入力の度に補完を発火できるかどうか確認する。

{Pos} = CodeMirror
watchChange = (cm) ->
  CodeMirror.showHint cm, ->
    cur   = cm.getCursor()
    token = cm.getTokenAt(cur)
    start = token.start
    end   = token.end
    from  = Pos(cur.line, start)
    to    = Pos(cur.line, end)

    if token.string is '@'
      list:[
        "@aaa"
        "@bbb"
        "@ccc"
        ]
      from: from
      to: to

cm = CodeMirror.fromTextArea el,
cm.on 'change', -> watchChange(cm)

この例だと @ を入力したら @aaa, @bbb, @ccc を候補に出る。

実践編

バッファ内からすべての単語を探し、入力中の文字列とマッチしたものを表示する。
(ここではunderscoreも使っているので注意)

{Pos} = CodeMirror
collectBufferWords = (cm, word) ->
  bufferWords = _.compact(cm.getValue().split(/\n|\s/))
  _.uniq _.filter bufferWords, (w) ->
    w.indexOf(word) > -1 and w isnt word

autocompleteAnyWords = (cm) ->
  CodeMirror.showHint cm, ->
    cur   = cm.getCursor()
    token = cm.getTokenAt(cur)
    start = token.start
    end   = token.end

    from  = Pos(cur.line, start)
    to    = Pos(cur.line, end)
    currentWord = token.string
    ch = cur.ch
    line = cur.line

    while --ch > -1
      t = cm.getTokenAt({ch, line})
      if t.string is ' '
        break
      currentWord= t.string + currentWord

    matchedWords = collectBufferWords(cm, currentWord)

    list: matchedWords
    from: {ch, line}
    to: to

CodeMirror.fromTextArea el,
  extraKeys:]
   'Tab': autocompleteAnyWords

これで冒頭の

ができるってワケ

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68