jenkins-bot has submitted this change and it was merged.

Change subject: Factor out wordbreak detection code and run it for simple 
insertions too
......................................................................


Factor out wordbreak detection code and run it for simple insertions too

Wordbreak detection was only done for "complex changes", not
for "simple insertions". This worked because we made sure to
always pawn at the edge of an splitOnWordbreak annotation
(through the forceContinuation property), so typing there is
a replacement (replacing a pawn), and that's considered complex
rather than simple.

However, to facilitate future changes as well as for general
non-WTF-y-ness, we should be consistent and also apply the
wordbreak logic for simple insertions. I still don't like
the wordbreak code that much, but at least this change makes
it look a bit less like it works completely by accident.

ve.ce.Surface#onContentChange:
* Factor out wordbreak logic to internal function
* Also call this function in the simple insertion code path
* Cache word break checks out of the loop
* Bail early if no word breaks detected
* Simplify complex change code by adding replacementRange variable
  (needed for call to filterForWordbreak() anyway)
* Replace instanceof check that's always true with length check
* Reuse values of previousStart and nextStart where possible
* Move computation of previousData, nextData and lengthDiff
  to initialization
* Rename dataString to nextDataString, now computed at
  initialization time rather than being recomputed in every
  single loop iteration
* Add TODO about sameLeadingAndTrailing being ignored

Change-Id: I9290782002dfc47f01c383278580ed0b754ab2e2
---
M src/ce/ve.ce.Surface.js
1 file changed, 71 insertions(+), 49 deletions(-)

Approvals:
  Esanders: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/src/ce/ve.ce.Surface.js b/src/ce/ve.ce.Surface.js
index 43440e9..f9d1541 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -1469,23 +1469,75 @@
  * @param {ve.Range} next.range New selection
  */
 ve.ce.Surface.prototype.onContentChange = function ( node, previous, next ) {
-       var data, range, len, annotations, offsetDiff, lengthDiff, 
sameLeadingAndTrailing,
-               previousStart, nextStart, newRange,
-               previousData, nextData,
-               i, length, annotation, annotationIndex, dataString,
-               annotationsLeft, annotationsRight,
+       var data, range, len, annotations, offsetDiff, sameLeadingAndTrailing,
+               previousStart, nextStart, newRange, replacementRange,
                fromLeft = 0,
                fromRight = 0,
-               nodeOffset = node.getModel().getOffset();
+               nodeOffset = node.getModel().getOffset(),
+               previousData = ve.splitClusters( previous.text ),
+               nextData = ve.splitClusters( next.text ),
+               lengthDiff = next.text.length - previous.text.length,
+               nextDataString = new ve.dm.DataString( nextData ),
+               surface = this;
+
+       /**
+        * Given a naïvely computed set of annotations to apply to the content 
we're about to insert,
+        * this function will check if we're inserting at a word break, check 
if there are any
+        * annotations in the set that need to be split at a word break, and 
remove those.
+        *
+        * @private
+        * @param {ve.dm.AnnotationSet} annotations Annotations to apply. Will 
be modified.
+        * @param {ve.Range} range Range covering removed content, or collapsed 
range at insertion offset.
+        */
+       function filterForWordbreak( annotations, range ) {
+               var i, length, annotation, annotationIndex, annotationsLeft, 
annotationsRight,
+                       left = range.start,
+                       right = range.end,
+                       // - nodeOffset - 1 to adjust from absolute to relative
+                       // adjustment from prev to next not needed because 
we're before the replacement
+                       breakLeft = unicodeJS.wordbreak.isBreak( 
nextDataString, left - nodeOffset - 1 ),
+                       // - nodeOffset - 1 to adjust from absolute to relative
+                       // + lengthDiff to adjust from prev to next
+                       breakRight = unicodeJS.wordbreak.isBreak( 
nextDataString, right + lengthDiff - nodeOffset - 1 );
+
+               if ( !breakLeft && !breakRight ) {
+                       // No word breaks either side, so nothing to do
+                       return;
+               }
+
+               annotationsLeft = 
surface.getModel().getDocument().data.getAnnotationsFromOffset( left - 1 );
+               annotationsRight = 
surface.getModel().getDocument().data.getAnnotationsFromOffset( right );
+
+               for ( i = 0, length = annotations.getLength(); i < length; i++ 
) {
+                       annotation = annotations.get( i );
+                       annotationIndex = annotations.getIndex( i );
+                       if (
+                               // This annotation splits on wordbreak, and...
+                               annotation.constructor.static.splitOnWordbreak 
&&
+                               (
+                                       // either we're at its right-hand 
boundary (its end is to our left) and
+                                       // there's a wordbreak to our left
+                                       ( breakLeft && 
!annotationsRight.containsIndex( annotationIndex ) ) ||
+                                       // or we're at its left-hand boundary 
(its beginning is to our right) and
+                                       // there's a wordbreak to our right
+                                       ( breakRight && 
!annotationsLeft.containsIndex( annotationIndex ) )
+                               )
+                       ) {
+                               annotations.removeAt( i );
+                               i--;
+                               length--;
+                       }
+               }
+       }
 
        if ( previous.range && next.range ) {
                offsetDiff = ( previous.range.isCollapsed() && 
next.range.isCollapsed() ) ?
                        next.range.start - previous.range.start : null;
-               lengthDiff = next.text.length - previous.text.length;
                previousStart = previous.range.start - nodeOffset - 1;
                nextStart = next.range.start - nodeOffset - 1;
                sameLeadingAndTrailing = offsetDiff !== null && (
                        // TODO: rewrite to static method with tests
+                       // TODO: actually start using the result again, or a 
modified version thereof
                        (
                                lengthDiff > 0 &&
                                previous.text.substring( 0, previousStart ) ===
@@ -1504,14 +1556,12 @@
 
                // Simple insertion
                if ( lengthDiff > 0 && offsetDiff === lengthDiff /* && 
sameLeadingAndTrailing */) {
-                       data = ve.splitClusters( next.text ).slice(
-                               previous.range.start - nodeOffset - 1,
-                               next.range.start - nodeOffset - 1
-                       );
+                       data = ve.splitClusters( next.text ).slice( 
previousStart, nextStart );
                        // Apply insertion annotations
                        annotations = this.model.getInsertionAnnotations();
-                       if ( annotations instanceof ve.dm.AnnotationSet ) {
-                               ve.dm.Document.static.addAnnotationsToData( 
data, this.model.getInsertionAnnotations() );
+                       if ( annotations.getLength() ) {
+                               filterForWordbreak( annotations, new ve.Range( 
previous.range.start ) );
+                               ve.dm.Document.static.addAnnotationsToData( 
data, annotations );
                        }
                        this.incRenderLock();
                        try {
@@ -1550,8 +1600,6 @@
 
        // Complex change
 
-       previousData = ve.splitClusters( previous.text );
-       nextData = ve.splitClusters( next.text );
        len = Math.min( previousData.length, nextData.length );
        // Count same characters from left
        while ( fromLeft < len && previousData[fromLeft] === nextData[fromLeft] 
) {
@@ -1565,35 +1613,16 @@
        ) {
                ++fromRight;
        }
+       replacementRange = new ve.Range(
+               nodeOffset + 1 + fromLeft,
+               nodeOffset + 1 + previousData.length - fromRight
+       );
        data = nextData.slice( fromLeft, nextData.length - fromRight );
+
        // Get annotations to the left of new content and apply
-       annotations = this.model.getDocument().data.getAnnotationsFromOffset( 
nodeOffset + 1 + fromLeft );
+       annotations = this.model.getDocument().data.getAnnotationsFromOffset( 
replacementRange.start );
        if ( annotations.getLength() ) {
-               annotationsLeft = 
this.model.getDocument().data.getAnnotationsFromOffset( nodeOffset + fromLeft );
-               annotationsRight = 
this.model.getDocument().data.getAnnotationsFromOffset( nodeOffset + 1 + 
previousData.length - fromRight );
-               for ( i = 0, length = annotations.getLength(); i < length; i++ 
) {
-                       annotation = annotations.get( i );
-                       annotationIndex = annotations.getIndex( i );
-                       if ( annotation.constructor.static.splitOnWordbreak ) {
-                               dataString = new ve.dm.DataString( nextData );
-                               if (
-                                       // if no annotation to the right, check 
for wordbreak
-                                       (
-                                               
!annotationsRight.containsIndex( annotationIndex ) &&
-                                               unicodeJS.wordbreak.isBreak( 
dataString, fromLeft )
-                                       ) ||
-                                       // if no annotation to the left, check 
for wordbreak
-                                       (
-                                               !annotationsLeft.containsIndex( 
annotationIndex ) &&
-                                               unicodeJS.wordbreak.isBreak( 
dataString, nextData.length - fromRight )
-                                       )
-                               ) {
-                                       annotations.removeAt( i );
-                                       i--;
-                                       length--;
-                               }
-                       }
-               }
+               filterForWordbreak( annotations, replacementRange );
                ve.dm.Document.static.addAnnotationsToData( data, annotations );
        }
        newRange = next.range;
@@ -1602,14 +1631,7 @@
        }
 
        this.changeModel(
-               ve.dm.Transaction.newFromReplacement(
-                       this.documentView.model,
-                       new ve.Range(
-                               nodeOffset + 1 + fromLeft,
-                               nodeOffset + 1 + previousData.length - fromRight
-                       ),
-                       data
-               ),
+               ve.dm.Transaction.newFromReplacement( this.documentView.model, 
replacementRange, data ),
                newRange
        );
 };

-- 
To view, visit https://gerrit.wikimedia.org/r/151004
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I9290782002dfc47f01c383278580ed0b754ab2e2
Gerrit-PatchSet: 2
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Catrope <roan.katt...@gmail.com>
Gerrit-Reviewer: Divec <da...@sheetmusic.org.uk>
Gerrit-Reviewer: Esanders <esand...@wikimedia.org>
Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to