diff --git a/src/Bindings/CodeMirrorBinding/CodeMirrorBinding.js b/src/Bindings/CodeMirrorBinding/CodeMirrorBinding.js new file mode 100644 index 00000000..87e3ffa6 --- /dev/null +++ b/src/Bindings/CodeMirrorBinding/CodeMirrorBinding.js @@ -0,0 +1,67 @@ +import Binding from '../Binding.js' + +function typeObserver (event) { + this._mutualExclude(() => { + const codeMirror = this.target; + var deltas = event.delta; + var index = 0; + var from = codeMirror.posFromIndex(index); + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (delta.retain) { + index = delta.retain; + from = codeMirror.posFromIndex(index); + } else if (delta.insert) { + codeMirror.replaceRange(delta.insert, from, from) + } else if (delta.delete) { + codeMirror.replaceRange('', from, codeMirror.posFromIndex(index + delta.delete)) + } + } + }); +} + +function codeMirrorObserver(codeMirror, deltas) { + this._mutualExclude(() => { + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i] + var start = codeMirror.indexFromPos(delta.from) + // apply the delete operation first + if (delta.removed.length > 0) { + var delLength = 0 + for (var j = 0; j < delta.removed.length; j++) { + delLength += delta.removed[j].length + } + // "enter" is also a character in our case + delLength += delta.removed.length - 1 + this.type.delete(start, delLength) + } + // apply insert operation + this.type.insert(start, delta.text.join('\n')) + } + }); +} + +/** + * A binding that binds a YText to a codemirror. + * + * This binding is automatically destroyed when its parent is deleted. + * + */ +export default class CodeMirrorBinding extends Binding { + constructor (textType, codeMirror) { + super(textType, codeMirror) + // set initial value + codeMirror.setValue(textType.toString()) + // Observers are handled by this class + this._typeObserver = typeObserver.bind(this) + this._codeMirrorObserver = codeMirrorObserver.bind(this) + textType.observe(this._typeObserver) + codeMirror.on('changes', this._codeMirrorObserver) + } + destroy () { + // Remove everything that is handled by this class + this.type.unobserve(this._typeObserver) + this.target.unobserve(this._codeMirrorObserver) + super.destroy() + } +} diff --git a/src/Y.dist.js b/src/Y.dist.js index fbf2445a..741a6e87 100644 --- a/src/Y.dist.js +++ b/src/Y.dist.js @@ -20,6 +20,7 @@ import { registerStruct } from './Util/structReferences.js' import TextareaBinding from './Bindings/TextareaBinding/TextareaBinding.js' import QuillBinding from './Bindings/QuillBinding/QuillBinding.js' import DomBinding from './Bindings/DomBinding/DomBinding.js' +import CodeMirrorBinding from './Bindings/CodeMirrorBinding/CodeMirrorBinding.js' import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js' import debug from 'debug' @@ -40,6 +41,7 @@ Y.XmlHook = YXmlHook Y.TextareaBinding = TextareaBinding Y.QuillBinding = QuillBinding Y.DomBinding = DomBinding +Y.CodeMirrorBinding = CodeMirrorBinding DomBinding.domToType = domToType DomBinding.domsToTypes = domsToTypes