@@ -29,16 +29,21 @@ var DiffArea = React.createClass({
29
29
30
30
var state = { } ;
31
31
32
+ // Create editors state
33
+ state . leftState = editorStateFromText ( left ) ;
34
+ state . rightState = editorStateFromText ( right ) ;
35
+
32
36
// Compute diff on whole texts
33
37
var diffs = computeDiff ( left , right ) ;
34
-
35
- // Make decorators
36
- var removedDecorator = createDiffsDecorator ( diffs , DIFF . REMOVED ) ;
37
- var insertedDecorator = createDiffsDecorator ( diffs , DIFF . INSERTED ) ;
38
-
39
- // Create editors state
40
- state . leftState = editorStateFromText ( left , removedDecorator ) ;
41
- state . rightState = editorStateFromText ( right , insertedDecorator ) ;
38
+ var mappingLeft = mapDiffsToBlocks ( diffs , DIFF . REMOVED , state . leftState . getCurrentContent ( ) . getBlockMap ( ) ) ;
39
+ var mappingRight = mapDiffsToBlocks ( diffs , DIFF . INSERTED , state . rightState . getCurrentContent ( ) . getBlockMap ( ) ) ;
40
+ // Update the decorators
41
+ state . leftState = Draft . EditorState . set ( state . leftState , {
42
+ decorator : createDiffsDecorator ( mappingLeft , DIFF . REMOVED )
43
+ } ) ;
44
+ state . rightState = Draft . EditorState . set ( state . rightState , {
45
+ decorator : createDiffsDecorator ( mappingRight , DIFF . INSERTED )
46
+ } ) ;
42
47
43
48
return state ;
44
49
} ,
@@ -68,12 +73,15 @@ var DiffArea = React.createClass({
68
73
69
74
var diffs = computeDiff ( left , right ) ;
70
75
76
+ var mappingLeft = mapDiffsToBlocks ( diffs , DIFF . REMOVED , this . state . leftState . getCurrentContent ( ) . getBlockMap ( ) ) ;
77
+ var mappingRight = mapDiffsToBlocks ( diffs , DIFF . INSERTED , this . state . rightState . getCurrentContent ( ) . getBlockMap ( ) ) ;
78
+
71
79
// Update the decorators
72
80
newState . leftState = Draft . EditorState . set ( this . state . leftState , {
73
- decorator : createDiffsDecorator ( diffs , DIFF . REMOVED )
81
+ decorator : createDiffsDecorator ( mappingLeft , DIFF . REMOVED )
74
82
} ) ;
75
83
newState . rightState = Draft . EditorState . set ( this . state . rightState , {
76
- decorator : createDiffsDecorator ( diffs , DIFF . INSERTED )
84
+ decorator : createDiffsDecorator ( mappingRight , DIFF . INSERTED )
77
85
} ) ;
78
86
this . setState ( newState ) ;
79
87
} ,
@@ -97,26 +105,71 @@ var DiffArea = React.createClass({
97
105
} ) ;
98
106
99
107
function editorStateFromText ( text , decorator ) {
100
- // For now, we can only work on a single content block.
101
- var content = Draft . convertFromRaw ( {
102
- blocks : [
103
- {
104
- text : text ,
105
- type : 'unstyled'
106
- }
107
- ] ,
108
- entityMap : { }
109
- } ) ;
108
+ var content = Draft . ContentState . createFromText ( text ) ;
110
109
return Draft . EditorState . createWithContent ( content , decorator ) ;
111
110
}
112
111
113
112
function computeDiff ( txt1 , txt2 ) {
114
- var diffs = DMP . diff_main ( txt1 , txt2 ) ;
115
- // Simplify diffs a bit to make it human readable (but non optimal)
116
- DMP . diff_cleanupSemantic ( diffs ) ;
113
+ var diffs = DMP . diff_wordMode ( txt1 , txt2 ) ;
117
114
return diffs ;
118
115
}
119
116
117
+ /**
118
+ * Returns the lists of highlighted ranges for each block of a blockMap
119
+ * @returns {Immutable.Map } blockKey -> { text, ranges: Array<{start, end}}>
120
+ */
121
+ function mapDiffsToBlocks ( diffs , type , blockMap ) {
122
+ var charIndex = 0 ;
123
+ var absoluteRanges = [ ] ;
124
+
125
+ diffs . forEach ( function ( diff ) {
126
+ var diffType = diff [ 0 ] ;
127
+ var diffText = diff [ 1 ] ;
128
+ if ( diffType === DIFF . EQUAL ) {
129
+ // No highlight. Move to next difference
130
+ charIndex += diffText . length ;
131
+ } else if ( diffType === type ) {
132
+ // Highlight, and move to next difference
133
+ absoluteRanges . push ( {
134
+ start : charIndex ,
135
+ end : charIndex + diffText . length
136
+ } ) ;
137
+ charIndex += diffText . length ;
138
+ } else {
139
+ // The diff text should not be in the contentBlock, so skip.
140
+ return ;
141
+ }
142
+ } ) ;
143
+
144
+ // `end` excluded
145
+ function findRangesBetween ( ranges , start , end ) {
146
+ var res = [ ] ;
147
+ ranges . forEach ( function ( range ) {
148
+ if ( range . start < end && range . end > start ) {
149
+ var intersectionStart = Math . max ( range . start , start ) ;
150
+ var intersectionEnd = Math . min ( range . end , end ) ;
151
+ // Push relative range
152
+ res . push ( {
153
+ start : intersectionStart - start ,
154
+ end : intersectionEnd - start
155
+ } ) ;
156
+ }
157
+ } ) ;
158
+ return res ;
159
+ }
160
+
161
+ var blockStartIndex = 0 ;
162
+ return blockMap . map ( function ( block ) {
163
+ var ranges = findRangesBetween ( absoluteRanges , blockStartIndex , blockStartIndex + block . getLength ( ) ) ;
164
+ blockStartIndex += block . getLength ( ) ;
165
+ return {
166
+ text : block . getText ( ) ,
167
+ key : block . getKey ( ) ,
168
+ ranges : ranges
169
+ } ;
170
+ } ) ;
171
+ }
172
+
120
173
// Decorators
121
174
122
175
var InsertedSpan = function ( props ) {
@@ -127,12 +180,11 @@ var RemovedSpan = function (props) {
127
180
} ;
128
181
129
182
/**
130
- * @param diffs The diff_match_patch result.
131
183
* @param type The type of diff to highlight
132
184
*/
133
- function createDiffsDecorator ( diffs , type ) {
185
+ function createDiffsDecorator ( mappedRanges , type ) {
134
186
return new Draft . CompositeDecorator ( [ {
135
- strategy : findDiff . bind ( undefined , diffs , type ) ,
187
+ strategy : findDiff . bind ( undefined , mappedRanges , type ) ,
136
188
component : type === DIFF . INSERTED ? InsertedSpan : RemovedSpan
137
189
} ] ) ;
138
190
}
@@ -141,23 +193,17 @@ function createDiffsDecorator(diffs, type) {
141
193
* Applies the decorator callback to all differences in the single content block.
142
194
* This needs to be cheap, because decorators are called often.
143
195
*/
144
- function findDiff ( diffs , type , contentBlock , callback ) {
145
- var charIndex = 0 ;
146
- diffs . forEach ( function ( diff ) {
147
- var diffType = diff [ 0 ] ;
148
- var diffText = diff [ 1 ] ;
149
- if ( diffType === DIFF . EQUAL ) {
150
- // No highlight. Move to next difference
151
- charIndex += diffText . length ;
152
- } else if ( diffType === type ) {
153
- // Highlight, and move to next difference
154
- callback ( charIndex , charIndex + diffText . length ) ;
155
- charIndex += diffText . length ;
156
- } else {
157
- // The diff text should not be in the contentBlock, so skip.
158
- return ;
196
+ function findDiff ( mappedRanges , type , contentBlock , callback ) {
197
+ var mapping = mappedRanges . get ( contentBlock . getKey ( ) ) ;
198
+ if ( mapping && mapping . text === contentBlock . getText ( ) ) {
199
+ mapping . ranges . forEach ( function ( range ) {
200
+ callback ( range . start , range . end ) ;
201
+ } ) ;
202
+ } else {
203
+ if ( mapping ) {
204
+ console . log ( 'Content changed' , mapping . key , contentBlock . getKey ( ) ) ;
159
205
}
160
- } ) ;
206
+ }
161
207
}
162
208
163
209
0 commit comments