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