Link page references in Scribunto modules

In Scribunto modules, link page names used in require(), mw.loadData()
and mw.loadJsonData() invocations.

Inspired from the CodeLinks gadget[0] but rewritten for brevity,
avoiding Wiktionary specific code, making the code more generic,
and thereby adding support for mw.loadJsonData().

[0]: https://en.wiktionary.org/wiki/MediaWiki:Gadget-CodeLinks.js

Bug: T368166
Change-Id: Idc554269ee52a05660fa41f065a2b3c73e2e1b9b
This commit is contained in:
Siddharth VP 2024-09-09 23:12:55 +05:30
parent e58d391b91
commit 4c9ed25619
3 changed files with 98 additions and 18 deletions

View file

@ -38,23 +38,6 @@
"pygments.wrapper.less" "pygments.wrapper.less"
] ]
}, },
"ext.pygments.view": {
"scripts": [
"pygments.linenumbers.js",
"pygments.links.js",
"pygments.copy.js"
],
"styles": [
"pygments.copy.css"
],
"messages": [
"syntaxhighlight-button-copy",
"syntaxhighlight-button-copied"
],
"dependencies": [
"mediawiki.util"
]
},
"ext.geshi.visualEditor": { "ext.geshi.visualEditor": {
"scripts": [ "scripts": [
"ve-syntaxhighlight/ve.dm.MWSyntaxHighlightNode.js", "ve-syntaxhighlight/ve.dm.MWSyntaxHighlightNode.js",
@ -96,7 +79,8 @@
"ParserFirstCallInit": "main", "ParserFirstCallInit": "main",
"ContentGetParserOutput": "main", "ContentGetParserOutput": "main",
"ApiFormatHighlight": "main", "ApiFormatHighlight": "main",
"SoftwareInfo": "main" "SoftwareInfo": "main",
"ResourceLoaderRegisterModules": "main"
}, },
"HookHandlers": { "HookHandlers": {
"main": { "main": {

View file

@ -32,6 +32,8 @@ use MediaWiki\MediaWikiServices;
use MediaWiki\Parser\Parser; use MediaWiki\Parser\Parser;
use MediaWiki\Parser\ParserOutput; use MediaWiki\Parser\ParserOutput;
use MediaWiki\Parser\Sanitizer; use MediaWiki\Parser\Sanitizer;
use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
use MediaWiki\ResourceLoader\ResourceLoader;
use MediaWiki\Status\Status; use MediaWiki\Status\Status;
use MediaWiki\Title\Title; use MediaWiki\Title\Title;
use ParserOptions; use ParserOptions;
@ -45,6 +47,7 @@ use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI;
class SyntaxHighlight extends ExtensionTagHandler implements class SyntaxHighlight extends ExtensionTagHandler implements
ParserFirstCallInitHook, ParserFirstCallInitHook,
ContentGetParserOutputHook, ContentGetParserOutputHook,
ResourceLoaderRegisterModulesHook,
ApiFormatHighlightHook, ApiFormatHighlightHook,
SoftwareInfoHook SoftwareInfoHook
{ {
@ -655,4 +658,32 @@ class SyntaxHighlight extends ExtensionTagHandler implements
// pass // pass
} }
} }
/**
* Hook to register ext.pygments.view module.
* @param ResourceLoader $rl
*/
public function onResourceLoaderRegisterModules( ResourceLoader $rl ): void {
$rl->register( 'ext.pygments.view', [
'localBasePath' => MW_INSTALL_PATH . '/extensions/SyntaxHighlight_GeSHi/modules',
'scripts' => array_merge( [
'pygments.linenumbers.js',
'pygments.links.js',
'pygments.copy.js'
], ExtensionRegistry::getInstance()->isLoaded( 'Scribunto' ) ? [
'pygments.links.scribunto.js'
] : [] ),
'styles' => [
'pygments.copy.css'
],
'messages' => [
'syntaxhighlight-button-copy',
'syntaxhighlight-button-copied'
],
'dependencies' => [
'mediawiki.util',
'mediawiki.Title'
]
] );
}
} }

View file

@ -0,0 +1,65 @@
$( () => {
const classes = {
singleQuoteString: 's1', doubleQuoteString: 's2'
};
function addLink( element, page ) {
const link = document.createElement( 'a' );
link.href = mw.util.getUrl( page );
// put text node from element inside link
const firstChild = element.firstChild;
if ( !( firstChild instanceof Text ) ) {
throw new TypeError( 'Expected Text object' );
}
link.appendChild( firstChild );
element.appendChild( link ); // put link inside syntax-highlighted string
}
// List of functions whose parameters should be linked if they meet the given condition
const parametersToLink = {
require: ( title ) => title.getNamespaceId() === 828,
'mw.loadData': ( title ) => title.getNamespaceId() === 828,
'mw.loadJsonData': () => true
};
const stringNodes = Array.from( document.getElementsByClassName( classes.singleQuoteString ) )
.concat( Array.from( document.getElementsByClassName( classes.doubleQuoteString ) ) );
stringNodes.forEach( ( node ) => {
if ( !node.nextElementSibling || !node.nextElementSibling.firstChild ||
node.nextElementSibling.firstChild.nodeValue.indexOf( ')' ) !== 0 ) {
return;
}
if ( !node.previousElementSibling || !node.previousElementSibling.firstChild ||
node.previousElementSibling.firstChild.nodeValue !== '(' ) {
return;
}
Object.keys( parametersToLink ).forEach( ( invocation ) => {
const parts = invocation.split( '.' );
let partIdx = parts.length - 1;
let curNode = node.previousElementSibling && node.previousElementSibling.previousElementSibling;
while ( partIdx >= 0 ) {
if ( !curNode || curNode.firstChild.nodeValue !== parts[ partIdx ] ) {
return;
}
if ( partIdx === 0 ) {
break;
}
const prev = curNode.previousElementSibling;
if ( !prev || prev.firstChild.nodeValue !== '.' ) {
return;
}
curNode = prev.previousElementSibling;
partIdx--;
}
const page = node.firstChild.nodeValue.slice( 1, -1 );
const condition = parametersToLink[ invocation ];
const title = mw.Title.newFromText( page );
if ( title && condition( title ) ) {
addLink( node, title.toText() );
}
} );
} );
} );