Permalink
Browse files

CodeMirror text editor plug-in.

- Based on the ace mode.
- Efficiently updates the DOM.
- Deals with insert and delete events.

Signed-off-by: Thaddee Tyl <thaddee.tyl@gmail.com>
  • Loading branch information...
1 parent b8fb5a8 commit 43e430e68bed1db5f3727d1f9c7eb717001d6f86 @espadrine espadrine committed Mar 19, 2012
Showing with 94 additions and 0 deletions.
  1. +94 −0 src/client/cm.coffee
View
94 src/client/cm.coffee
@@ -0,0 +1,94 @@
+# This is some utility code to connect a CodeMirror editor
+# to a sharejs document.
+# It is heavily inspired from the Ace editor hook.
+
+# Convert a CodeMirror delta into an op understood by share.js
+applyToShareJS = (editorDoc, delta, doc) ->
+ # CodeMirror deltas give a text replacement.
+ # I tuned this operation a little bit, for speed.
+ startPos = 0 # Get character position from # of chars in each line.
+ i = 0 # i goes through all lines.
+
+ while i < delta.from.line
+ startPos += editorDoc.lineInfo(i).text.length + 1 # Add 1 for '\n'
+ i++
+
+ startPos += delta.from.ch
+
+ if delta.to.line == delta.from.line &&
+ delta.to.ch == delta.from.ch # Then nothing was removed.
+ doc.insert startPos, delta.text.join '\n'
+ else
+ delLen = delta.to.ch - delta.from.ch
+ while i < delta.to.line
+ delLen += editorDoc.lineInfo(i).text.length + 1 # Add 1 for '\n'
+ i++
+ doc.del startPos, delLen
+ doc.insert startPos, delta.text.join '\n' if delta.text
+
+ applyToShareJS editorDoc, delta.next, doc if delta.next
+
+# Attach a CodeMirror editor to the document. The editor's contents are replaced
+# with the document's contents unless keepEditorContents is true. (In which case
+# the document's contents are nuked and replaced with the editor's).
+window.sharejs.Doc::attach_cm = (editor, keepEditorContents) ->
+ unless @provides.text
+ throw new Error 'Only text documents can be attached to CodeMirror2'
+
+ sharedoc = @
+ check = ->
+ window.setTimeout ->
+ editorText = editor.getValue()
+ otText = sharedoc.getText()
+
+ if editorText != otText
+ console.error "Text does not match!"
+ console.error "editor: #{editorText}"
+ console.error "ot: #{otText}"
+ # Replace the editor text with the doc snapshot.
+ editor.setValue sharedoc.snapshot
+ , 0
+
+ if keepEditorContents
+ @del 0, sharedoc.getText().length
+ @insert 0, editor.getValue()
+ else
+ editor.setValue sharedoc.getText()
+
+ check()
+
+ # When we apply ops from sharejs, CodeMirror emits edit events.
+ # We need to ignore those to prevent an infinite typing loop.
+ suppress = false
+
+ # Listen for edits in CodeMirror.
+ editorListener = (ed, change) ->
+ return if suppress
+ applyToShareJS editor, change, sharedoc
+
+ check()
+
+ editor.setOption 'onChange', editorListener
+
+ @on 'insert', (pos, text) ->
+ suppress = true
+ # All the primitives we need are already in CM's API.
+ editor.replaceRange text, editor.posFromIndex(pos)
+ suppress = false
+ check()
+
+ @on 'delete', (pos, text) ->
+ suppress = true
+ from = editor.posFromIndex pos
+ to = editor.posFromIndex (pos + text.length)
+ editor.replaceRange '', from, to
+ suppress = false
+ check()
+
+ @detach_ace = ->
+ # TODO: can we remove the insert and delete event callbacks?
+ editor.setOption 'onChange', null
+ delete @detach_ace
+
+ return
+

0 comments on commit 43e430e

Please sign in to comment.