Codemirror

CodeMirrorでAutoCompleteする

More than 3 years have passed since last update.

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

これで冒頭の

ができるってワケ