jenkins-bot has submitted this change and it was merged.
Change subject: fix bold and italic apostrophes (v 3.2)
......................................................................
fix bold and italic apostrophes (v 3.2)
Bug: T108455
Change-Id: Ie9e0d734004d062e4c347f7940eb34bdc231d026
---
M extension.json
M resources/mode/mediawiki/mediawiki.css
M resources/mode/mediawiki/mediawiki.js
3 files changed, 141 insertions(+), 46 deletions(-)
Approvals:
Pastakhov: Looks good to me, approved
Florianschmidtwelzow: Looks good to me, but someone else must approve
jenkins-bot: Verified
diff --git a/extension.json b/extension.json
index 5c73a5f..907c317 100644
--- a/extension.json
+++ b/extension.json
@@ -1,6 +1,6 @@
{
"name": "CodeMirror",
- "version": "3.1.14",
+ "version": "3.2",
"author": [
"[https://www.mediawiki.org/wiki/User:Pastakhov Pavel
Astakhov]",
"[https://www.mediawiki.org/wiki/User:Florianschmidtwelzow
Florian Schmidt]"
diff --git a/resources/mode/mediawiki/mediawiki.css
b/resources/mode/mediawiki/mediawiki.css
index 197d764..aae2300 100644
--- a/resources/mode/mediawiki/mediawiki.css
+++ b/resources/mode/mediawiki/mediawiki.css
@@ -19,7 +19,7 @@
.cm-mw-indenting {color: #08f; font-weight: bold; background-color: #ddd;}
.cm-mw-mnemonic {color: #090;}
.cm-mw-comment {color: #aaa; font-weight: normal;}
-.cm-mw-apostrophes {color: #08f;}
+.cm-mw-apostrophes-bold, .cm-mw-apostrophes-italic {color: #08f;}
pre.cm-mw-section-1 {font-size: 1.8em;}
pre.cm-mw-section-2 {font-size: 1.5em;}
diff --git a/resources/mode/mediawiki/mediawiki.js
b/resources/mode/mediawiki/mediawiki.js
index 0eca7f4..60b65fb 100644
--- a/resources/mode/mediawiki/mediawiki.js
+++ b/resources/mode/mediawiki/mediawiki.js
@@ -22,23 +22,24 @@
CodeMirror.defineMode( 'mediawiki', function( config/*, parserConfig */ ) {
- var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' );
- var permittedHtmlTags = {'b': true, 'bdi': true, 'del': true, 'i':
true, 'ins': true,
- 'u': true, 'font': true, 'big': true, 'small': true, 'sub':
true, 'sup': true,
- 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true,
'h6': true, 'cite': true,
- 'code': true, 'em': true, 's': true, 'strike': true, 'strong':
true, 'tt': true,
- 'var': true, 'div': true, 'center': true, 'blockquote': true,
'ol': true, 'ul': true,
- 'dl': true, 'table': true, 'caption': true, 'pre': true,
'ruby': true, 'rb': true,
- 'rp': true, 'rt': true, 'rtc': true, 'p': true, 'span': true,
'abbr': true, 'dfn': true,
- 'kbd': true, 'samp': true, 'data': true, 'time': true, 'mark':
true, 'br': true,
- 'wbr': true, 'hr': true, 'li': true, 'dt': true, 'dd': true,
'td': true, 'th': true,
- 'tr': true, 'noinclude': true, 'includeonly': true,
'onlyinclude': true};
+ var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' ),
+ permittedHtmlTags = {'b': true, 'bdi': true, 'del': true, 'i':
true, 'ins': true,
+ 'u': true, 'font': true, 'big': true, 'small': true,
'sub': true, 'sup': true,
+ 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5':
true, 'h6': true, 'cite': true,
+ 'code': true, 'em': true, 's': true, 'strike': true,
'strong': true, 'tt': true,
+ 'var': true, 'div': true, 'center': true, 'blockquote':
true, 'ol': true, 'ul': true,
+ 'dl': true, 'table': true, 'caption': true, 'pre':
true, 'ruby': true, 'rb': true,
+ 'rp': true, 'rt': true, 'rtc': true, 'p': true, 'span':
true, 'abbr': true, 'dfn': true,
+ 'kbd': true, 'samp': true, 'data': true, 'time': true,
'mark': true, 'br': true,
+ 'wbr': true, 'hr': true, 'li': true, 'dt': true, 'dd':
true, 'td': true, 'th': true,
+ 'tr': true, 'noinclude': true, 'includeonly': true,
'onlyinclude': true},
+ isBold, isItalic, firstsingleletterword, firstmultiletterword,
firstspace, mBold, mItalic, mTokens = [], mStyle;
function makeStyle( style, state, endGround ) {
- if ( state.isBold ) {
+ if ( isBold ) {
style += ' strong';
}
- if ( state.isItalic ) {
+ if ( isItalic ) {
style += ' em';
}
return makeLocalStyle( style, state, endGround );
@@ -351,25 +352,25 @@
}
function eatLinkText() {
- var isBold, isItalic;
+ var linkIsBold, linkIsItalic;
return function ( stream, state ) {
if ( stream.match( ']]' ) ) {
state.tokenize = state.stack.pop();
return makeLocalStyle( 'mw-link-bracket',
state, 'nLink' );
}
if ( stream.match( '\'\'\'' ) ) {
- isBold = (isBold ? false : true);
+ linkIsBold = (linkIsBold ? false : true);
return makeLocalStyle( 'mw-link-text
mw-apostrophes', state );
}
if ( stream.match( '\'\'' ) ) {
- isItalic = (isItalic ? false : true);
+ linkIsItalic = (linkIsItalic ? false : true);
return makeLocalStyle( 'mw-link-text
mw-apostrophes', state );
}
var tmpstyle = 'mw-link-text';
- if ( isBold ) {
+ if ( linkIsBold ) {
tmpstyle += ' strong';
}
- if ( isItalic ) {
+ if ( linkIsItalic ) {
tmpstyle += ' em';
}
if ( stream.match( /[^'\]\{\&~]+/ ) ) {
@@ -457,9 +458,10 @@
function eatExtTagArea( name ) {
return function( stream, state ) {
- var origString = false, from = stream.pos, to;
- var pattern = new RegExp( '</' + name + '\\s*>' );
- var m = pattern.exec( from ? stream.string.slice( from
) : stream.string );
+ var origString = false, from = stream.pos, to,
+ pattern = new RegExp( '</' + name + '\\s*>' ),
+ m = pattern.exec( from ? stream.string.slice(
from ) : stream.string );
+
if ( m ) {
if ( m.index === 0 ) {
state.tokenize = eatExtCloseTag( name );
@@ -474,6 +476,7 @@
origString = stream.string;
stream.string = origString.slice( 0, to );
}
+
state.stack.push( state.tokenize );
state.tokenize = eatExtTokens( origString );
return state.tokenize( stream, state );
@@ -525,13 +528,9 @@
}
function inTableCaption( stream, state ) {
- if ( stream.sol() ) {
- state.isBold = false;
- state.isItalic = false;
- if ( stream.match( /[\s\u00a0]*[\|!]/, false ) ) {
- state.tokenize = inTable;
- return inTable( stream, state );
- }
+ if ( stream.sol() && stream.match( /[\s\u00a0]*[\|!]/, false )
) {
+ state.tokenize = inTable;
+ return inTable( stream, state );
}
return eatWikiText( 'mw-table-caption', '' )( stream, state );
}
@@ -570,8 +569,6 @@
function eatTableRow( isStart, isHead ) {
return function ( stream, state ) {
if ( stream.sol() ) {
- state.isBold = false;
- state.isItalic = false;
if ( stream.match( /[\s\u00a0]*[\|!]/, false )
) {
state.tokenize = inTable;
return inTable( stream, state );
@@ -581,8 +578,8 @@
return makeStyle( (isHead ? 'strong' :
''), state );
}
if ( stream.match( '||' ) || isHead &&
stream.match( '!!' ) || (isStart && stream.eat( '|' )) ) {
- state.isBold = false;
- state.isItalic = false;
+ isBold = false;
+ isItalic = false;
if ( isStart ) {
state.tokenize = eatTableRow(
false, isHead );
}
@@ -636,8 +633,6 @@
var ch, sol = stream.sol();
if ( sol ) {
- state.isBold = false;
- state.isItalic = false;
if ( stream.match( urlProtocols ) ) { //
highlight free external links, bug T108448
state.stack.push( state.tokenize );
state.tokenize = eatFreeExternalLink;
@@ -704,12 +699,18 @@
case '&':
return makeStyle( eatMnemonic( stream,
style, mnemonicStyle ), state );
case '\'':
- if ( stream.match( '\'\'' ) ) {
- state.isBold = state.isBold ?
false : true;
- return makeLocalStyle(
'mw-apostrophes', state );
- } else if ( stream.eat( '\'' ) ) {
- state.isItalic = state.isItalic
? false : true;
- return makeLocalStyle(
'mw-apostrophes', state );
+ if ( stream.match( /'*(?=''''')/ ) ||
stream.match( /'''(?!')/, false ) ) { // skip the irrelevant apostrophes ( >5
or =4 )
+ break;
+ }
+ if ( stream.match( '\'\'' ) ) { // bold\
+ if ( !(firstsingleletterword ||
stream.match( '\'\'', false )) ) {
+
prepareItalicForCorrection( stream );
+ }
+ isBold = isBold ? false : true;
+ return makeLocalStyle(
'mw-apostrophes-bold', state );
+ } else if ( stream.eat( '\'' ) ) { //
italic
+ isItalic = isItalic ? false :
true;
+ return makeLocalStyle(
'mw-apostrophes-italic', state );
}
break;
case '[':
@@ -815,17 +816,50 @@
};
}
+ /**
+ * Remembers position and status for rollbacking.
+ * It needed for change bold to italic with apostrophe before it if
required
+ * @see https://phabricator.wikimedia.org/T108455
+ * @param CodeMirror.StringStream stream
+ * @returns null
+ */
+ function prepareItalicForCorrection( stream ) {
+ // see Parser::doQuotes() in MediaWiki core, it works similar
+ // firstsingleletterword has maximum priority
+ // firstmultiletterword has medium priority
+ // firstspace has low priority
+ var end = stream.pos,
+ str = stream.string.substr( 0, end - 3 ),
+ x1 = str.substr( -1, 1 ),
+ x2 = str.substr( -2, 1 );
+
+ // firstsingleletterword olways is undefined here
+ if ( x1 === ' ' ) {
+ if ( firstmultiletterword || firstspace ) {
+ return;
+ }
+ firstspace = end;
+ } else if ( x2 === ' ' ) {
+ firstsingleletterword = end;
+ } else if ( firstmultiletterword ) {
+ return;
+ } else {
+ firstmultiletterword = end;
+ }
+ // remember bold and italic state for restore
+ mBold = isBold;
+ mItalic = isItalic;
+ }
+
return {
startState: function() {
- return { tokenize: eatWikiText('', ''), stack: [],
InHtmlTag:[], isBold: false, isItalic: false, extName: false, extMode: false,
extState: false, nTemplate: 0, nLink: 0, nExt: 0 };
+ return { tokenize: eatWikiText('', ''), stack: [],
InHtmlTag:[], extName: false, extMode: false, extState: false, nTemplate: 0,
nLink: 0, nExt: 0 };
},
copyState: function( state ) {
return {
tokenize: state.tokenize,
stack: state.stack.concat( [] ),
InHtmlTag: state.InHtmlTag.concat( [] ),
- isBold: state.isBold,
- isItalic: state.isItalic,
extName: state.extName,
extMode: state.extMode,
extState: state.extMode !== false &&
CodeMirror.copyState( state.extMode, state.extState ),
@@ -835,7 +869,68 @@
};
},
token: function( stream, state ) {
- return state.tokenize( stream, state );
+ var style, p, t, f,
+ readyTokens = [],
+ tmpTokens = [];
+
+ if ( mTokens.length > 0 ) { // just send saved tokens
till they exists
+ t = mTokens.shift();
+ stream.pos = t.pos;
+ state = t.state;
+ return t.style;
+ }
+
+ if ( stream.sol() ) { // reset bold and italic status
in every new line
+ isBold = false;
+ isItalic = false;
+ firstsingleletterword = undefined;
+ firstmultiletterword = undefined;
+ firstspace = undefined;
+ }
+
+ do {
+ style = state.tokenize( stream, state ); // get
token style
+ f = firstsingleletterword ||
firstmultiletterword || firstspace;
+ if ( f ) { // rollback point exists
+ if ( f !== p ) { // new rollbak point
+ p = f;
+ if ( tmpTokens.length > 0 ) {
// it's not first rollbak point
+ readyTokens =
readyTokens.concat( tmpTokens ); // save tokens
+ tmpTokens = [];
+ }
+ }
+ tmpTokens.push( { // save token
+ pos: stream.pos,
+ style: style,
+ state: CodeMirror.copyState(
state.extMode ? state.extMode : 'mediawiki', state )
+ } );
+ } else { // rollback point not exists
+ mStyle = style; // remember style
before possible rollback point
+ return style; // just return token style
+ }
+ } while ( !stream.eol() );
+
+ if ( isBold && isItalic ) { // needs to rollback
+ isItalic = mItalic; // restore status
+ isBold = mBold;
+ firstsingleletterword = undefined;
+ firstmultiletterword = undefined;
+ firstspace = undefined;
+ if ( readyTokens.length > 0 ) { // it contains
tickets before the point of rollback
+
readyTokens[readyTokens.length-1].pos++; // add one apostrophe, next token will
be italic (two apostrophes)
+ mTokens = readyTokens; // for sending
tokens till the point of rollback
+ } else { // there are no tikets before the
point of rollback
+ stream.pos = tmpTokens[0].pos - 2; //
eat( '\'')
+ return mStyle; // send saved Style
+ }
+ } else { // not needs to rollback
+ mTokens = readyTokens.concat( tmpTokens ); //
send all saved tokens
+ }
+ // return first saved token
+ t = mTokens.shift();
+ stream.pos = t.pos;
+ state = t.state;
+ return t.style;
},
blankLine: function( state ) {
if ( state.extName ) {
--
To view, visit https://gerrit.wikimedia.org/r/232447
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ie9e0d734004d062e4c347f7940eb34bdc231d026
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/extensions/CodeMirror
Gerrit-Branch: master
Gerrit-Owner: Pastakhov <[email protected]>
Gerrit-Reviewer: Florianschmidtwelzow <[email protected]>
Gerrit-Reviewer: Pastakhov <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits