codemirror-ot

Real-time collaboration plugin for CodeMirror 6. For background & writeup, see Medium: Codemirror 6 Experiments. Overhauled in May 2022 to work with the latest CodeMirror 6 APIs and JSON1. A fully functioning collaborative editor that leverages this library can be found in VZCode.
At its core this library is a translator between Operational Transformation and CodeMirror 6. This is one piece of the puzzle for enabling real-time collaboration on text documents using CodeMirror and ShareDB.
Overview
codemirror-ot
provides seamless integration between CodeMirror 6 and Operational Transformation (OT) systems, enabling real-time collaborative editing. The library handles the complex translation between CodeMirror's change representation and OT operations for both JSON0 and JSON1 OT types.
Key Features
- Bidirectional Translation: Convert between CodeMirror changes and OT operations
- Multiple OT Type Support: Works with both JSON0 and JSON1 operational transformation types
- Unicode Support: Proper handling of Unicode characters including emojis
- Path-based Operations: Support for operations at specific document paths
- ShareDB Integration: Ready-to-use ViewPlugin for ShareDB collaboration
- Hot Module Reloading: Maintains state during development
Installation
npm install codemirror-ot
Quick Start
Basic ShareDB Integration
import { EditorView } from '@codemirror/view';
import { EditorState } from '@codemirror/state';
import { json1Sync } from 'codemirror-ot';
import json1 from 'ot-json1';
import textUnicode from 'ot-text-unicode';
const view = new EditorView({
state: EditorState.create({
doc: shareDBDoc.data.content.files['myfile.js'].text,
extensions: [
json1Sync({
shareDBDoc,
path: ['content', 'files', 'myfile.js', 'text'],
json1,
textUnicode,
debug: false,
}),
],
}),
});
Manual Translation
import {
changesToOpJSON0,
changesToOpJSON1,
opToChangesJSON0,
opToChangesJSON1,
} from 'codemirror-ot';
import { EditorState, ChangeSet } from '@codemirror/state';
const state = EditorState.create({ doc: 'Hello World' });
const changes = ChangeSet.of(
[{ from: 5, to: 6, insert: '-' }],
state.doc.length,
);
const json0Op = changesToOpJSON0([], changes, state.doc);
const json1Op = changesToOpJSON1([], changes, state.doc, json1, textUnicode);
const changesFromJSON0 = opToChangesJSON0(json0Op);
const changesFromJSON1 = opToChangesJSON1(json1Op, 'Hello World');
API Reference
Translation Functions
changesToOpJSON0(path, changeSet, doc)
Converts a CodeMirror ChangeSet to a JSON0 OT operation.
Parameters:
path
- string[]
- Path array for nested document operations
changeSet
- ChangeSet
- CodeMirror ChangeSet containing the changes
doc
- Text
- The original document before changes
Returns: JSON0Op[]
- Array of JSON0 operation components
Example:
const op = changesToOpJSON0(['files', 'index.js'], changeSet, state.doc);
changesToOpJSON1(path, changeSet, doc, json1, textUnicode)
Converts a CodeMirror ChangeSet to a JSON1 OT operation with proper Unicode handling.
Parameters:
path
- string[]
- Path array for nested document operations
changeSet
- ChangeSet
- CodeMirror ChangeSet containing the changes
doc
- Text
- The original document before changes
json1
- JSON1Type
- JSON1 OT type instance
textUnicode
- TextUnicodeType
- Text-unicode OT type instance
Returns: JSON1Op | null
- JSON1 operation or null for no-ops
Example:
const op = changesToOpJSON1(
['files', 'index.js'],
changeSet,
state.doc,
json1,
textUnicode,
);
opToChangesJSON0(op)
Converts a JSON0 OT operation to CodeMirror changes.
Parameters:
op
- JSON0Op[]
- Array of JSON0 operation components
Returns: Change[]
- Array of CodeMirror change objects
Example:
const changes = opToChangesJSON0([
{ p: [5], sd: ' ' },
{ p: [5], si: '-' },
]);
opToChangesJSON1(op, originalDoc?)
Converts a JSON1 OT operation to CodeMirror changes with Unicode position conversion.
Parameters:
op
- JSON1Op
- JSON1 operation
originalDoc
- string
(optional) - Original document for Unicode position conversion
Returns: Change[]
- Array of CodeMirror change objects
Example:
const changes = opToChangesJSON1([{ es: [5, '-', { d: ' ' }] }], 'Hello World');
Integration
json1Sync(options)
Creates a CodeMirror ViewPlugin that synchronizes with ShareDB using JSON1 operations.
Parameters:
options
- Object
shareDBDoc
- ShareDB document instance
path
- string[]
(default: []) - Path to the text content in the document
json1
- JSON1 OT type instance
textUnicode
- Text-unicode OT type instance
debug
- boolean
(default: false) - Enable debug logging
Returns: ViewPlugin
- CodeMirror ViewPlugin for real-time collaboration
Example:
const syncPlugin = json1Sync({
shareDBDoc: myShareDBDoc,
path: ['content', 'files', 'main.js', 'text'],
json1,
textUnicode,
debug: true,
});
Features:
- Bidirectional Sync: Automatically syncs changes between CodeMirror and ShareDB
- Multi-part Operations: Handles complex operations with multiple components
- Path Filtering: Only processes operations that affect the specified path
- Lock Mechanism: Prevents infinite loops during synchronization
- Hot Module Reloading: Maintains editor state during development updates
Utilities
canOpAffectPath(op, path)
Determines if an OT operation can affect content at the specified path.
Parameters:
op
- JSON1Op | null
- The operation to check
path
- string[]
- The path to check against
Returns: boolean
- True if the operation affects the path
Example:
const canAffect = canOpAffectPath(
['content', 'files', 'main.js', 'text', { es: [5, 'hello'] }],
['content', 'files', 'main.js', 'text'],
);
Unicode Support
The library properly handles Unicode characters, including emojis and multi-byte characters, by converting between UTF-16 positions (used by CodeMirror) and Unicode code point positions (used by text-unicode OT type).
const changes = opToChangesJSON1(
[{ es: [2, 'World', { d: 'Hello' }] }],
'🚀 Hello',
);
Error Handling
The library includes robust error handling for:
- Null or undefined operations
- Invalid path structures
- Unicode conversion edge cases
- Malformed OT operations
Testing
Run the test suite:
npm test
The test suite includes comprehensive coverage of:
- String insertions, deletions, and replacements
- Unicode character handling
- Path-based operations
- Multi-part operations
- Real-world collaboration scenarios
Related Projects
- CodeMirror 6 - The text editor this library integrates with
- ShareDB - Real-time database with OT support
- JSON1 - JSON operational transformation type
- VZCode - Collaborative code editor using this library
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
)
- Add tests for your changes
- Ensure all tests pass (
npm test
)
- Commit your changes (
git commit -m 'Add some amazing feature'
)
- Push to the branch (
git push origin feature/amazing-feature
)
- Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.