Cscott has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/230144

Change subject: WIP: support RegExp sequences; trigger sequence matcher after 
newline.
......................................................................

WIP: support RegExp sequences; trigger sequence matcher after newline.

Allow a Sequence to be specified using a RegExp, but hard-limit the size
of the string checked by the RegExp to avoid O(N^2) blowup when inserting
N characters of text.

Tweak the signatures of the sequence match methods to return the range of
the matching portion of the data, to avoid having to repeat the text
extraction and regexp match when executing the associated command.

Trigger `checkSequences` in `handleLinearEnter` so that sequences
are checked when enter is pressed (pressing enter doesn't trigger
onSurfaceObserverContentChange).

Change-Id: I4d03d2f7b437ea4a289a4c5cee2a081998228319
---
M src/ce/ve.ce.Surface.js
M src/ui/ve.ui.Sequence.js
M src/ui/ve.ui.SequenceRegistry.js
3 files changed, 41 insertions(+), 12 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/44/230144/1

diff --git a/src/ce/ve.ce.Surface.js b/src/ce/ve.ce.Surface.js
index e65f4bb..a9871df 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -2826,7 +2826,8 @@
 
        // sequences.length will likely be 0 or 1 so don't cache
        for ( i = 0; i < sequences.length; i++ ) {
-               executed = sequences[i].execute( this.surface ) || executed;
+               // XXX pass sequence[i].range to the Command somehow.
+               executed = sequences[i].sequence.execute( this.surface ) || 
executed;
        }
        if ( executed ) {
                this.showModelSelection( model.getSelection() );
@@ -3418,7 +3419,8 @@
                stack = [],
                outermostNode = null,
                nodeModel = null,
-               nodeModelRange = null;
+               nodeModelRange = null,
+               surface = this;
 
        // Handle removal first
        if ( !range.isCollapsed() ) {
@@ -3579,6 +3581,9 @@
        }
        // Reset and resume polling
        this.surfaceObserver.clear();
+       setTimeout( function () {
+               surface.checkSequences();
+       } );
 };
 
 /**
diff --git a/src/ui/ve.ui.Sequence.js b/src/ui/ve.ui.Sequence.js
index b5fca70..c3fa554 100644
--- a/src/ui/ve.ui.Sequence.js
+++ b/src/ui/ve.ui.Sequence.js
@@ -12,7 +12,7 @@
  * @constructor
  * @param {string} name Symbolic name
  * @param {string} commandName Command name this sequence executes
- * @param {string|Array} data Data to match
+ * @param {string|Array|RegExp} data Data to match
  * @param {number} [strip] Number of data elements to strip after execution 
(from the right)
  */
 ve.ui.Sequence = function VeUiSequence( name, commandName, data, strip ) {
@@ -31,23 +31,28 @@
 /**
  * Check if the sequence matches a given offset in the data
  *
- * @param {string|Array} data String or linear data
+ * @param {ve.dm.ElementLinearData} data String or linear data
  * @param {number} offset Offset
- * @return {boolean} Sequence matches
+ * @return {ve.Range|null} Range corresponding to the match, or else null
  */
-ve.ui.Sequence.prototype.match = function ( data, offset ) {
+ve.ui.Sequence.prototype.match = function ( data, offset, plaintext ) {
        var i, j = offset - 1;
 
+       if ( $.type( this.data ) === 'regexp' ) {
+               i = plaintext.search( this.data );
+               return ( i < 0 ) ? null :
+                       new ve.Range( offset - plaintext.length + i, offset );
+       }
        for ( i = this.data.length - 1; i >= 0; i--, j-- ) {
                if ( typeof this.data[i] === 'string' ) {
                        if ( this.data[i] !== data.getCharacterData( j ) ) {
-                               return false;
+                               return null;
                        }
                } else if ( !ve.compare( this.data[i], data.getData( j ), true 
) ) {
-                       return false;
+                       return null;
                }
        }
-       return true;
+       return new ve.Range( offset - this.data.length, offset );
 };
 
 /**
diff --git a/src/ui/ve.ui.SequenceRegistry.js b/src/ui/ve.ui.SequenceRegistry.js
index 47c6f5a..1598db6 100644
--- a/src/ui/ve.ui.SequenceRegistry.js
+++ b/src/ui/ve.ui.SequenceRegistry.js
@@ -46,10 +46,29 @@
  * @return {ve.ui.Sequence[]} Sequences which match
  */
 ve.ui.SequenceRegistry.prototype.findMatching = function ( data, offset ) {
-       var name, sequences = [];
+       var textStart, plaintext, name, range, sequences = [];
+       // To avoid blowup when matching RegExp sequences, we're going to grab
+       // all the plaintext to the left (until the nearest node) *once* and 
pass
+       // it to each sequence matcher.  We're also going to hard-limit that
+       // plaintext to 256 characters to ensure we don't run into O(N^2)
+       // slowdown when inserting N characters of plain text.
+       for (textStart = offset - 1; textStart >= 0 && (offset - textStart) <= 
256; textStart--) {
+               // Ignore an element if it occurs in the last two context 
characters.
+               // Typing "foo\n" creates "foo</p><p>" in the data model, and 
we want
+               // to give the matcher a chance against it.
+               if ( data.isElementData( textStart ) && (offset - textStart) > 
2 ) {
+                       break;
+               }
+       }
+       plaintext = data.getText( true, new ve.Range( textStart + 1, offset ) );
+       // Now search through the registry.
        for ( name in this.registry ) {
-               if ( this.registry[name].match( data, offset ) ) {
-                       sequences.push( this.registry[name] );
+               range = this.registry[name].match( data, offset, plaintext );
+               if ( range !== null ) {
+                       sequences.push( {
+                               sequence: this.registry[name],
+                               range: range
+                       } );
                }
        }
        return sequences;

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I4d03d2f7b437ea4a289a4c5cee2a081998228319
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Cscott <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to