CodeMirrorModeMediaWikiConfig: add missing tokens for nested templates

Nested templates have background shading relative to their level of
nesting. See the newly added test case as an example. Without these
tokens registered, the styling won't show properly.

Since these tokens aren't referenced directly by the StreamParser, nor
do they have a parent Tag, we don't need them as constants like we do
for other tokens.

Bug: T348019
Change-Id: I87bb99d538344957987b2bd88f902a1427a36522
This commit is contained in:
MusikAnimal 2024-02-27 21:03:46 -05:00
parent 7002bc434d
commit 321d462311
5 changed files with 76 additions and 118 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -16,27 +16,39 @@ class CodeMirrorModeMediaWikiConfig {
} }
/** /**
* Register a tag in CodeMirror. The generated CSS class will be of the form 'cm-mw-ext-tagname' * Register a token for the given tag in CodeMirror. The generated CSS class will be of
* This is for internal use to dynamically register tags from other MediaWiki extensions. * the form 'cm-mw-ext-tagname'. This is for internal use to dynamically register tags
* from other MediaWiki extensions.
* *
* @see https://www.mediawiki.org/wiki/Extension:CodeMirror#Extension_integration * @see https://www.mediawiki.org/wiki/Extension:CodeMirror#Extension_integration
* @param {string} tag * @param {string} tag
* @param {Tag} parent * @param {Tag} [parent]
* @internal * @internal
*/ */
addTag( tag, parent ) { addTag( tag, parent = null ) {
if ( this.tokenTable[ `mw-tag-${ tag }` ] ) { if ( this.tokenTable[ `mw-tag-${ tag }` ] ) {
return; return;
} }
this.tokenTable[ `mw-tag-${ tag }` ] = Tag.define( parent ); this.addToken( `mw-tag-${ tag }`, parent );
this.tokenTable[ `mw-ext-${ tag }` ] = Tag.define( parent ); this.addToken( `mw-ext-${ tag }`, parent );
}
/**
* Dynamically register a token in CodeMirror.
* This is solely for use by this.addTag() and CodeMirrorModeMediaWiki.makeLocalStyle().
*
* @param {string} token
* @param {Tag} [parent]
* @internal
*/
addToken( token, parent = null ) {
if ( this.tokenTable[ token ] ) {
return;
}
this.tokenTable[ token ] = Tag.define( parent );
this.extHighlightStyles.push( { this.extHighlightStyles.push( {
tag: this.tokenTable[ `mw-tag-${ tag }` ], tag: this.tokenTable[ token ],
class: `cm-mw-tag-${ tag }` class: `cm-${ token }`
} );
this.extHighlightStyles.push( {
tag: this.tokenTable[ `mw-ext-${ tag }` ],
class: `cm-mw-ext-${ tag }`
} ); } );
} }
@ -152,7 +164,6 @@ class CodeMirrorModeMediaWikiConfig {
return { return {
em: 'mw-em', em: 'mw-em',
error: 'mw-error', error: 'mw-error',
extGround: 'mw-ext-ground',
extNowiki: 'mw-ext-nowiki', extNowiki: 'mw-ext-nowiki',
extPre: 'mw-ext-pre', extPre: 'mw-ext-pre',
extTag: 'mw-exttag', extTag: 'mw-exttag',
@ -163,7 +174,6 @@ class CodeMirrorModeMediaWikiConfig {
freeExtLinkProtocol: 'mw-free-extlink-protocol', freeExtLinkProtocol: 'mw-free-extlink-protocol',
htmlEntity: 'mw-html-entity', htmlEntity: 'mw-html-entity',
link: 'mw-link', link: 'mw-link',
linkGround: 'mw-link-ground',
linkPageName: 'mw-link-pagename', linkPageName: 'mw-link-pagename',
nowiki: 'mw-tag-nowiki', nowiki: 'mw-tag-nowiki',
pageName: 'mw-pagename', pageName: 'mw-pagename',
@ -172,14 +182,7 @@ class CodeMirrorModeMediaWikiConfig {
skipFormatting: 'mw-skipformatting', skipFormatting: 'mw-skipformatting',
strong: 'mw-strong', strong: 'mw-strong',
tableCaption: 'mw-table-caption', tableCaption: 'mw-table-caption',
templateGround: 'mw-template-ground', templateVariableDelimiter: 'mw-templatevariable-delimiter'
templateExtGround: 'mw-template-ext-ground',
templateLinkGround: 'mw-template-link-ground',
templateVariableDelimiter: 'mw-templatevariable-delimiter',
template2ExtGround: 'mw-template2-ext-ground',
template2Ground: 'mw-template2-ground',
template3ExtGround: 'mw-template3-ext-ground',
template3Ground: 'mw-template3-ground'
}; };
} }
@ -197,7 +200,6 @@ class CodeMirrorModeMediaWikiConfig {
return { return {
[ this.tags.em ]: Tag.define(), [ this.tags.em ]: Tag.define(),
[ this.tags.error ]: Tag.define(), [ this.tags.error ]: Tag.define(),
[ this.tags.extGround ]: Tag.define(),
[ this.tags.extNowiki ]: Tag.define(), [ this.tags.extNowiki ]: Tag.define(),
[ this.tags.extPre ]: Tag.define(), [ this.tags.extPre ]: Tag.define(),
[ this.tags.extTag ]: Tag.define(), [ this.tags.extTag ]: Tag.define(),
@ -208,7 +210,6 @@ class CodeMirrorModeMediaWikiConfig {
[ this.tags.freeExtLinkProtocol ]: Tag.define(), [ this.tags.freeExtLinkProtocol ]: Tag.define(),
[ this.tags.htmlEntity ]: Tag.define(), [ this.tags.htmlEntity ]: Tag.define(),
[ this.tags.link ]: Tag.define(), [ this.tags.link ]: Tag.define(),
[ this.tags.linkGround ]: Tag.define(),
[ this.tags.linkPageName ]: Tag.define(), [ this.tags.linkPageName ]: Tag.define(),
[ this.tags.nowiki ]: Tag.define(), [ this.tags.nowiki ]: Tag.define(),
[ this.tags.pageName ]: Tag.define(), [ this.tags.pageName ]: Tag.define(),
@ -217,14 +218,7 @@ class CodeMirrorModeMediaWikiConfig {
[ this.tags.skipFormatting ]: Tag.define(), [ this.tags.skipFormatting ]: Tag.define(),
[ this.tags.strong ]: Tag.define(), [ this.tags.strong ]: Tag.define(),
[ this.tags.tableCaption ]: Tag.define(), [ this.tags.tableCaption ]: Tag.define(),
[ this.tags.templateGround ]: Tag.define(), [ this.tags.templateVariableDelimiter ]: Tag.define()
[ this.tags.templateExtGround ]: Tag.define(),
[ this.tags.templateLinkGround ]: Tag.define(),
[ this.tags.templateVariableDelimiter ]: Tag.define(),
[ this.tags.template2ExtGround ]: Tag.define(),
[ this.tags.template2Ground ]: Tag.define(),
[ this.tags.template3ExtGround ]: Tag.define(),
[ this.tags.template3Ground ]: Tag.define()
}; };
} }
@ -419,10 +413,6 @@ class CodeMirrorModeMediaWikiConfig {
tag: context.tokenTable[ this.tags.error ], tag: context.tokenTable[ this.tags.error ],
class: 'cm-mw-error' class: 'cm-mw-error'
}, },
{
tag: context.tokenTable[ this.tags.extGround ],
class: 'cm-mw-ext-ground'
},
{ {
tag: context.tokenTable[ this.tags.extNowiki ], tag: context.tokenTable[ this.tags.extNowiki ],
class: 'cm-mw-ext-nowiki' class: 'cm-mw-ext-nowiki'
@ -459,10 +449,6 @@ class CodeMirrorModeMediaWikiConfig {
tag: context.tokenTable[ this.tags.htmlEntity ], tag: context.tokenTable[ this.tags.htmlEntity ],
class: 'cm-mw-html-entity' class: 'cm-mw-html-entity'
}, },
{
tag: context.tokenTable[ this.tags.linkGround ],
class: 'cm-mw-link-ground'
},
{ {
tag: context.tokenTable[ this.tags.linkPageName ], tag: context.tokenTable[ this.tags.linkPageName ],
class: 'cm-mw-link-pagename' class: 'cm-mw-link-pagename'
@ -495,38 +481,10 @@ class CodeMirrorModeMediaWikiConfig {
tag: context.tokenTable[ this.tags.tableCaption ], tag: context.tokenTable[ this.tags.tableCaption ],
class: 'cm-mw-table-caption' class: 'cm-mw-table-caption'
}, },
{
tag: context.tokenTable[ this.tags.templateGround ],
class: 'cm-mw-template-ground'
},
{
tag: context.tokenTable[ this.tags.templateExtGround ],
class: 'cm-mw-template-ext-ground'
},
{
tag: context.tokenTable[ this.tags.templateLinkGround ],
class: 'cm-mw-template-link-ground'
},
{ {
tag: context.tokenTable[ this.tags.templateVariableDelimiter ], tag: context.tokenTable[ this.tags.templateVariableDelimiter ],
class: 'cm-mw-templatevariable-delimiter' class: 'cm-mw-templatevariable-delimiter'
}, },
{
tag: context.tokenTable[ this.tags.template2ExtGround ],
class: 'cm-mw-template2-ext-ground'
},
{
tag: context.tokenTable[ this.tags.template2Ground ],
class: 'cm-mw-template2-ground'
},
{
tag: context.tokenTable[ this.tags.template3ExtGround ],
class: 'cm-mw-template3-ext-ground'
},
{
tag: context.tokenTable[ this.tags.template3Ground ],
class: 'cm-mw-template3-ground'
},
...this.extHighlightStyles ...this.extHighlightStyles
]; ];

View file

@ -33,11 +33,53 @@ class CodeMirrorModeMediaWiki {
this.tokens = []; this.tokens = [];
this.oldTokens = []; this.oldTokens = [];
this.tokenTable = modeConfig.tokenTable; this.tokenTable = modeConfig.tokenTable;
this.registerGroundTokens();
// Dynamically register any tags that aren't already in CodeMirrorModeMediaWikiConfig // Dynamically register any tags that aren't already in CodeMirrorModeMediaWikiConfig
Object.keys( this.config.tags ).forEach( ( tag ) => modeConfig.addTag( tag ) ); Object.keys( this.config.tags ).forEach( ( tag ) => modeConfig.addTag( tag ) );
} }
/**
* Register the ground tokens. These aren't referenced directly in the StreamParser, nor do
* they have a parent Tag, so we don't need them as constants like we do for other tokens.
* See this.makeLocalStyle() for how these tokens are used.
*/
registerGroundTokens() {
[
'mw-ext-ground',
'mw-ext-link-ground',
'mw-ext2-ground',
'mw-ext2-link-ground',
'mw-ext3-ground',
'mw-ext3-link-ground',
'mw-link-ground',
'mw-template-ext-ground',
'mw-template-ext-link-ground',
'mw-template-ext2-ground',
'mw-template-ext2-link-ground',
'mw-template-ext3-ground',
'mw-template-ext3-link-ground',
'mw-template-ground',
'mw-template-link-ground',
'mw-template2-ext-ground',
'mw-template2-ext-link-ground',
'mw-template2-ext2-ground',
'mw-template2-ext2-link-ground',
'mw-template2-ext3-ground',
'mw-template2-ext3-link-ground',
'mw-template2-ground',
'mw-template2-link-ground',
'mw-template3-ext-ground',
'mw-template3-ext-link-ground',
'mw-template3-ext2-ground',
'mw-template3-ext2-link-ground',
'mw-template3-ext3-ground',
'mw-template3-ext3-link-ground',
'mw-template3-ground',
'mw-template3-link-ground'
].forEach( ( ground ) => modeConfig.addToken( ground ) );
}
eatHtmlEntity( stream, style ) { eatHtmlEntity( stream, style ) {
let ok; let ok;
if ( stream.eat( '#' ) ) { if ( stream.eat( '#' ) ) {
@ -67,44 +109,6 @@ class CodeMirrorModeMediaWiki {
makeLocalStyle( style, state, endGround ) { makeLocalStyle( style, state, endGround ) {
let ground = ''; let ground = '';
/**
* List out token names in a comment for search purposes.
*
* Tokens used here include:
* - mw-ext-ground
* - mw-ext-link-ground
* - mw-ext2-ground
* - mw-ext2-link-ground
* - mw-ext3-ground
* - mw-ext3-link-ground
* - mw-link-ground
* - mw-template-ext-ground
* - mw-template-ext-link-ground
* - mw-template-ext2-ground
* - mw-template-ext2-link-ground
* - mw-template-ext3-ground
* - mw-template-ext3-link-ground
* - mw-template-link-ground
* - mw-template2-ext-ground
* - mw-template2-ext-link-ground
* - mw-template2-ext2-ground
* - mw-template2-ext2-link-ground
* - mw-template2-ext3-ground
* - mw-template2-ext3-link-ground
* - mw-template2-ground
* - mw-template2-link-ground
* - mw-template3-ext-ground
* - mw-template3-ext-link-ground
* - mw-template3-ext2-ground
* - mw-template3-ext2-link-ground
* - mw-template3-ext3-ground
* - mw-template3-ext3-link-ground
* - mw-template3-ground
* - mw-template3-link-ground
*
* NOTE: these should be defined in CodeMirrorModeMediaWikiConfig.tokenTable()
* and CodeMirrorModeMediaWikiConfig.highlightStyle()
*/
switch ( state.nTemplate ) { switch ( state.nTemplate ) {
case 0: case 0:
break; break;

View file

@ -148,6 +148,11 @@ const testCases = [
input: 'Soft­hyphen\nzero-widthspace\nnon-breaking space\nnarrownbsp', input: 'Soft­hyphen\nzero-widthspace\nnon-breaking space\nnarrownbsp',
// i18n messages are the keys because we don't stub mw.msg() in this test. // i18n messages are the keys because we don't stub mw.msg() in this test.
output: '<div class="cm-line">Soft<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-specialChar" title="codemirror-control-character" aria-label="codemirror-control-character">•</span><img class="cm-widgetBuffer" aria-hidden="true">hyphen</div><div class="cm-line">zero-width<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-specialChar" title="codemirror-special-char-zero-width-space" aria-label="codemirror-special-char-zero-width-space">•</span><img class="cm-widgetBuffer" aria-hidden="true">space</div><div class="cm-line">non-breaking<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-special-char-nbsp" title="codemirror-special-char-nbsp" aria-label="codemirror-special-char-nbsp">·</span><img class="cm-widgetBuffer" aria-hidden="true">space</div><div class="cm-line">narrow<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-special-char-nbsp" title="codemirror-special-char-narrow-nbsp" aria-label="codemirror-special-char-narrow-nbsp">·</span><img class="cm-widgetBuffer" aria-hidden="true">nbsp</div>' output: '<div class="cm-line">Soft<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-specialChar" title="codemirror-control-character" aria-label="codemirror-control-character">•</span><img class="cm-widgetBuffer" aria-hidden="true">hyphen</div><div class="cm-line">zero-width<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-specialChar" title="codemirror-special-char-zero-width-space" aria-label="codemirror-special-char-zero-width-space">•</span><img class="cm-widgetBuffer" aria-hidden="true">space</div><div class="cm-line">non-breaking<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-special-char-nbsp" title="codemirror-special-char-nbsp" aria-label="codemirror-special-char-nbsp">·</span><img class="cm-widgetBuffer" aria-hidden="true">space</div><div class="cm-line">narrow<img class="cm-widgetBuffer" aria-hidden="true"><span class="cm-special-char-nbsp" title="codemirror-special-char-narrow-nbsp" aria-label="codemirror-special-char-narrow-nbsp">·</span><img class="cm-widgetBuffer" aria-hidden="true">nbsp</div>'
},
{
title: 'Nested template calls',
input: '{{foo|{{bar|[[Test]]|{{baz|[[Test2]]}}}}}}',
output: '<div class="cm-line"><span class="cm-mw-template-ground cm-mw-template-bracket">{{</span><span class="cm-mw-template-ground cm-mw-pagename cm-mw-template-name">foo</span><span class="cm-mw-template-ground cm-mw-template-delimiter">|</span><span class="cm-mw-template2-ground cm-mw-template-bracket">{{</span><span class="cm-mw-template2-ground cm-mw-pagename cm-mw-template-name">bar</span><span class="cm-mw-template2-ground cm-mw-template-delimiter">|</span><span class="cm-mw-template2-link-ground cm-mw-link-bracket">[[</span><span class="cm-mw-template2-link-ground cm-mw-link-pagename cm-mw-pagename">Test</span><span class="cm-mw-template2-link-ground cm-mw-link-bracket">]]</span><span class="cm-mw-template2-ground cm-mw-template-delimiter">|</span><span class="cm-mw-template3-ground cm-mw-template-bracket">{{</span><span class="cm-mw-template3-ground cm-mw-pagename cm-mw-template-name">baz</span><span class="cm-mw-template3-ground cm-mw-template-delimiter">|</span><span class="cm-mw-template3-link-ground cm-mw-link-bracket">[[</span><span class="cm-mw-template3-link-ground cm-mw-link-pagename cm-mw-pagename">Test2</span><span class="cm-mw-template3-link-ground cm-mw-link-bracket">]]</span><span class="cm-mw-template3-ground cm-mw-template-bracket">}}</span><span class="cm-mw-template2-ground cm-mw-template-bracket">}}</span><span class="cm-mw-template-ground cm-mw-template-bracket">}}</span></div>'
} }
]; ];
@ -242,7 +247,6 @@ describe( 'CodeMirrorModeMediaWiki', () => {
// Custom tags // Custom tags
'em', 'em',
'error', 'error',
'extGround',
'extNowiki', 'extNowiki',
'extPre', 'extPre',
'extTag', 'extTag',
@ -253,7 +257,6 @@ describe( 'CodeMirrorModeMediaWiki', () => {
'freeExtLinkProtocol', 'freeExtLinkProtocol',
'htmlEntity', 'htmlEntity',
'link', 'link',
'linkGround',
'linkPageName', 'linkPageName',
'nowiki', 'nowiki',
'pageName', 'pageName',
@ -262,14 +265,7 @@ describe( 'CodeMirrorModeMediaWiki', () => {
'skipFormatting', 'skipFormatting',
'strong', 'strong',
'tableCaption', 'tableCaption',
'templateGround', 'templateVariableDelimiter'
'templateExtGround',
'templateLinkGround',
'templateVariableDelimiter',
'template2ExtGround',
'template2Ground',
'template3ExtGround',
'template3Ground'
] ); ] );
} ); } );
} ); } );