/
ace.coffee
131 lines (98 loc) · 3.59 KB
/
ace.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# This is some utility code to connect an ace editor to a sharejs document.
requireImpl = if ace.require? then ace.require else require
Range = requireImpl("ace/range").Range
# Convert an ace delta into an op understood by share.js
applyToShareJS = (editorDoc, delta, doc) ->
# Get the start position of the range, in no. of characters
getStartOffsetPosition = (range) ->
# This is quite inefficient - getLines makes a copy of the entire
# lines array in the document. It would be nice if we could just
# access them directly.
lines = editorDoc.getLines 0, range.start.row
offset = 0
for line, i in lines
offset += if i < range.start.row
line.length
else
range.start.column
# Add the row number to include newlines.
offset + range.start.row
pos = getStartOffsetPosition(delta.range)
switch delta.action
when 'insertText' then doc.insert pos, delta.text
when 'removeText' then doc.del pos, delta.text.length
when 'insertLines'
text = delta.lines.join('\n') + '\n'
doc.insert pos, text
when 'removeLines'
text = delta.lines.join('\n') + '\n'
doc.del pos, text.length
else throw new Error "unknown action: #{delta.action}"
return
# Attach an ace 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.extendDoc 'attach_ace', (editor, keepEditorContents) ->
throw new Error 'Only text documents can be attached to ace' unless @provides['text']
doc = this
editorDoc = editor.getSession().getDocument()
editorDoc.setNewLineMode 'unix'
check = ->
window.setTimeout ->
editorText = editorDoc.getValue()
otText = doc.getText()
if editorText != otText
console.error "Text does not match!"
console.error "editor: #{editorText}"
console.error "ot: #{otText}"
# Should probably also replace the editor text with the doc snapshot.
, 0
if keepEditorContents
doc.del 0, doc.getText().length
doc.insert 0, editorDoc.getValue()
else
editorDoc.setValue doc.getText()
check()
# When we apply ops from sharejs, ace emits edit events. We need to ignore those
# to prevent an infinite typing loop.
suppress = false
# Listen for edits in ace
editorListener = (change) ->
return if suppress
applyToShareJS editorDoc, change.data, doc
check()
editorDoc.on 'change', editorListener
# Listen for remote ops on the sharejs document
docListener = (op) ->
suppress = true
applyToDoc editorDoc, op
suppress = false
check()
# Horribly inefficient.
offsetToPos = (offset) ->
# Again, very inefficient.
lines = editorDoc.getAllLines()
row = 0
for line, row in lines
break if offset <= line.length
# +1 for the newline.
offset -= lines[row].length + 1
row:row, column:offset
doc.on 'insert', insertListener = (pos, text) ->
suppress = true
editorDoc.insert offsetToPos(pos), text
suppress = false
check()
doc.on 'delete', deleteListener = (pos, text) ->
suppress = true
range = Range.fromPoints offsetToPos(pos), offsetToPos(pos + text.length)
editorDoc.remove range
suppress = false
check()
doc.detach_ace = ->
doc.removeListener 'insert', insertListener
doc.removeListener 'delete', deleteListener
doc.removeListener 'remoteop', docListener
editorDoc.removeListener 'change', editorListener
delete doc.detach_ace
return