summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SyntaxHighlight_GeSHi/includes
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/SyntaxHighlight_GeSHi/includes
first commit
Diffstat (limited to 'www/wiki/extensions/SyntaxHighlight_GeSHi/includes')
-rw-r--r--www/wiki/extensions/SyntaxHighlight_GeSHi/includes/GeSHi.php42
-rw-r--r--www/wiki/extensions/SyntaxHighlight_GeSHi/includes/ResourceLoaderSyntaxHighlightVisualEditorModule.php70
-rw-r--r--www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlight.php577
-rw-r--r--www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightAce.php174
-rw-r--r--www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightGeSHiCompat.php113
5 files changed, 976 insertions, 0 deletions
diff --git a/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/GeSHi.php b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/GeSHi.php
new file mode 100644
index 00000000..516de12f
--- /dev/null
+++ b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/GeSHi.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Stub class for maintaining backward-compatibility with extensions
+ * that have not been updated for version 2.0 of SyntaxHighlight_GeSHi.
+ */
+class GeSHi {
+
+ private $html;
+
+ public function __construct( $html ) {
+ $this->html = $html;
+ }
+
+ public function error() {
+ }
+
+ public function set_language( $language ) {
+ }
+
+ public function parse_code() {
+ global $wgOut;
+ $wgOut->addModuleStyles( 'ext.pygments' );
+ return $this->html;
+ }
+}
diff --git a/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/ResourceLoaderSyntaxHighlightVisualEditorModule.php b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/ResourceLoaderSyntaxHighlightVisualEditorModule.php
new file mode 100644
index 00000000..720cfd22
--- /dev/null
+++ b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/ResourceLoaderSyntaxHighlightVisualEditorModule.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+class ResourceLoaderSyntaxHighlightVisualEditorModule extends ResourceLoaderFileModule {
+
+ protected $targets = [ 'desktop', 'mobile' ];
+
+ /**
+ * @param ResourceLoaderContext $context
+ * @return string JavaScript code
+ */
+ public function getScript( ResourceLoaderContext $context ) {
+ $scripts = parent::getScript( $context );
+
+ return $scripts . Xml::encodeJsCall(
+ 've.dm.MWSyntaxHighlightNode.static.addPygmentsLanguages', [
+ $this->getPygmentsLanguages()
+ ],
+ ResourceLoader::inDebugMode()
+ ) . Xml::encodeJsCall(
+ 've.dm.MWSyntaxHighlightNode.static.addGeshiToPygmentsMap', [
+ SyntaxHighlightGeSHiCompat::getGeSHiToPygmentsMap()
+ ],
+ ResourceLoader::inDebugMode()
+ ) . Xml::encodeJsCall(
+ 've.dm.MWSyntaxHighlightNode.static.addPygmentsToAceMap', [
+ SyntaxHighlightAce::getPygmentsToAceMap()
+ ],
+ ResourceLoader::inDebugMode()
+ );
+ }
+
+ /**
+ * Don't break debug mode by only showing file URLs
+ * @param ResourceLoaderContext $context
+ * @return array
+ */
+ public function getScriptURLsForDebug( ResourceLoaderContext $context ) {
+ return ResourceLoaderModule::getScriptURLsForDebug( $context );
+ }
+
+ /**
+ * Get a full list of available languages
+ * @return array
+ */
+ private function getPygmentsLanguages() {
+ $lexers = require __DIR__ . '/../SyntaxHighlight.lexers.php';
+ return $lexers;
+ }
+
+ public function enableModuleContentVersion() {
+ return true;
+ }
+}
diff --git a/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlight.php b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlight.php
new file mode 100644
index 00000000..076efe1e
--- /dev/null
+++ b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlight.php
@@ -0,0 +1,577 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+use MediaWiki\Shell\Shell;
+
+class SyntaxHighlight {
+
+ /** @var int The maximum number of lines that may be selected for highlighting. **/
+ const HIGHLIGHT_MAX_LINES = 1000;
+
+ /** @var int Maximum input size for the highlighter (100 kB). **/
+ const HIGHLIGHT_MAX_BYTES = 102400;
+
+ /** @var string CSS class for syntax-highlighted code. **/
+ const HIGHLIGHT_CSS_CLASS = 'mw-highlight';
+
+ /** @var int Cache version. Increment whenever the HTML changes. */
+ const CACHE_VERSION = 2;
+
+ /** @var array Mapping of MIME-types to lexer names. **/
+ private static $mimeLexers = [
+ 'text/javascript' => 'javascript',
+ 'application/json' => 'javascript',
+ 'text/xml' => 'xml',
+ ];
+
+ /**
+ * Get the Pygments lexer name for a particular language.
+ *
+ * @param string $lang Language name.
+ * @return string|null Lexer name, or null if no matching lexer.
+ */
+ private static function getLexer( $lang ) {
+ static $lexers = null;
+
+ if ( $lang === null ) {
+ return null;
+ }
+
+ if ( !$lexers ) {
+ $lexers = require __DIR__ . '/../SyntaxHighlight.lexers.php';
+ }
+
+ $lexer = strtolower( $lang );
+
+ if ( in_array( $lexer, $lexers ) ) {
+ return $lexer;
+ }
+
+ $geshi2pygments = SyntaxHighlightGeSHiCompat::getGeSHiToPygmentsMap();
+
+ // Check if this is a GeSHi lexer name for which there exists
+ // a compatible Pygments lexer with a different name.
+ if ( isset( $geshi2pygments[$lexer] ) ) {
+ $lexer = $geshi2pygments[$lexer];
+ if ( in_array( $lexer, $lexers ) ) {
+ return $lexer;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Register parser hook
+ *
+ * @param Parser &$parser
+ */
+ public static function onParserFirstCallInit( Parser &$parser ) {
+ foreach ( [ 'source', 'syntaxhighlight' ] as $tag ) {
+ $parser->setHook( $tag, [ 'SyntaxHighlight', 'parserHook' ] );
+ }
+ }
+
+ /**
+ * Parser hook
+ *
+ * @param string $text
+ * @param array $args
+ * @param Parser $parser
+ * @return string
+ * @throws MWException
+ */
+ public static function parserHook( $text, $args, $parser ) {
+ global $wgUseTidy;
+
+ // Replace strip markers (For e.g. {{#tag:syntaxhighlight|<nowiki>...}})
+ $out = $parser->mStripState->unstripNoWiki( $text );
+
+ // Don't trim leading spaces away, just the linefeeds
+ $out = preg_replace( '/^\n+/', '', rtrim( $out ) );
+
+ // Convert deprecated attributes
+ if ( isset( $args['enclose'] ) ) {
+ if ( $args['enclose'] === 'none' ) {
+ $args['inline'] = true;
+ }
+ unset( $args['enclose'] );
+ }
+
+ $lexer = isset( $args['lang'] ) ? $args['lang'] : '';
+
+ $result = self::highlight( $out, $lexer, $args );
+ if ( !$result->isGood() ) {
+ $parser->addTrackingCategory( 'syntaxhighlight-error-category' );
+ }
+ $out = $result->getValue();
+
+ // HTML Tidy will convert tabs to spaces incorrectly (bug 30930).
+ // But the conversion from tab to space occurs while reading the input,
+ // before the conversion from &#9; to tab, so we can armor it that way.
+ if ( $wgUseTidy ) {
+ $out = str_replace( "\t", '&#9;', $out );
+ }
+
+ // Allow certain HTML attributes
+ $htmlAttribs = Sanitizer::validateAttributes( $args, [ 'style', 'class', 'id', 'dir' ] );
+ if ( !isset( $htmlAttribs['class'] ) ) {
+ $htmlAttribs['class'] = self::HIGHLIGHT_CSS_CLASS;
+ } else {
+ $htmlAttribs['class'] .= ' ' . self::HIGHLIGHT_CSS_CLASS;
+ }
+ if ( !( isset( $htmlAttribs['dir'] ) && $htmlAttribs['dir'] === 'rtl' ) ) {
+ $htmlAttribs['dir'] = 'ltr';
+ }
+
+ if ( isset( $args['inline'] ) ) {
+ // Enforce inlineness. Stray newlines may result in unexpected list and paragraph processing
+ // (also known as doBlockLevels()).
+ $out = str_replace( "\n", ' ', $out );
+ $out = Html::rawElement( 'code', $htmlAttribs, $out );
+
+ } else {
+ // Not entirely sure what benefit this provides, but it was here already
+ $htmlAttribs['class'] .= ' ' . 'mw-content-' . $htmlAttribs['dir'];
+
+ // Unwrap Pygments output to provide our own wrapper. We can't just always use the 'nowrap'
+ // option (pass 'inline'), since it disables other useful things like line highlighting.
+ // Tolerate absence of quotes for Html::element() and wgWellFormedXml=false.
+ if ( $out !== '' ) {
+ $m = [];
+ if ( preg_match( '/^<div class="?mw-highlight"?>(.*)<\/div>$/s', trim( $out ), $m ) ) {
+ $out = trim( $m[1] );
+ } else {
+ throw new MWException( 'Unexpected output from Pygments encountered' );
+ }
+ }
+
+ // Use 'nowiki' strip marker to prevent list processing (also known as doBlockLevels()).
+ // However, leave the wrapping <div/> outside to prevent <p/>-wrapping.
+ $marker = $parser::MARKER_PREFIX . '-syntaxhighlightinner-' .
+ sprintf( '%08X', $parser->mMarkerIndex++ ) . $parser::MARKER_SUFFIX;
+ $parser->mStripState->addNoWiki( $marker, $out );
+
+ $out = Html::openElement( 'div', $htmlAttribs ) .
+ $marker .
+ Html::closeElement( 'div' );
+ }
+
+ // Register CSS
+ $parser->getOutput()->addModuleStyles( 'ext.pygments' );
+
+ return $out;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getPygmentizePath() {
+ global $wgPygmentizePath;
+
+ // If $wgPygmentizePath is unset, use the bundled copy.
+ if ( $wgPygmentizePath === false ) {
+ $wgPygmentizePath = __DIR__ . '/../pygments/pygmentize';
+ }
+
+ return $wgPygmentizePath;
+ }
+
+ /**
+ * @param string $code
+ * @param bool $inline
+ */
+ private static function plainCodeWrap( $code, $inline ) {
+ if ( $inline ) {
+ return htmlspecialchars( $code, ENT_NOQUOTES );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [ 'class' => self::HIGHLIGHT_CSS_CLASS ],
+ Html::element( 'pre', [], $code )
+ );
+ }
+
+ /**
+ * Highlight a code-block using a particular lexer.
+ *
+ * @param string $code Code to highlight.
+ * @param string|null $lang Language name, or null to use plain markup.
+ * @param array $args Associative array of additional arguments.
+ * If it contains a 'line' key, the output will include line numbers.
+ * If it includes a 'highlight' key, the value will be parsed as a
+ * comma-separated list of lines and line-ranges to highlight.
+ * If it contains a 'start' key, the value will be used as the line at which to
+ * start highlighting.
+ * If it contains a 'inline' key, the output will not be wrapped in `<div><pre/></div>`.
+ * @return Status Status object, with HTML representing the highlighted
+ * code as its value.
+ */
+ public static function highlight( $code, $lang = null, $args = [] ) {
+ $status = new Status;
+
+ $lexer = self::getLexer( $lang );
+ if ( $lexer === null && $lang !== null ) {
+ $status->warning( 'syntaxhighlight-error-unknown-language', $lang );
+ }
+
+ // For empty tag, output nothing instead of empty <pre>.
+ if ( $code === '' ) {
+ $status->value = '';
+ return $status;
+ }
+
+ $length = strlen( $code );
+ if ( strlen( $code ) > self::HIGHLIGHT_MAX_BYTES ) {
+ // Disable syntax highlighting
+ $lexer = null;
+ $status->warning(
+ 'syntaxhighlight-error-exceeds-size-limit',
+ $length,
+ self::HIGHLIGHT_MAX_BYTES
+ );
+ } elseif ( Shell::isDisabled() ) {
+ // Disable syntax highlighting
+ $lexer = null;
+ $status->warning( 'syntaxhighlight-error-pygments-invocation-failure' );
+ wfWarn(
+ 'MediaWiki determined that it cannot invoke Pygments. ' .
+ 'As a result, SyntaxHighlight_GeSHi will not perform any syntax highlighting. ' .
+ 'See the debug log for details: ' .
+ 'https://www.mediawiki.org/wiki/Manual:$wgDebugLogFile'
+ );
+ }
+
+ $inline = isset( $args['inline'] );
+
+ if ( $inline ) {
+ $code = trim( $code );
+ }
+
+ if ( $lexer === null ) {
+ // When syntax highlighting is disabled..
+ $status->value = self::plainCodeWrap( $code, $inline );
+ return $status;
+ }
+
+ $options = [
+ 'cssclass' => self::HIGHLIGHT_CSS_CLASS,
+ 'encoding' => 'utf-8',
+ ];
+
+ // Line numbers
+ if ( isset( $args['line'] ) ) {
+ $options['linenos'] = 'inline';
+ }
+
+ if ( $lexer === 'php' && strpos( $code, '<?php' ) === false ) {
+ $options['startinline'] = 1;
+ }
+
+ // Highlight specific lines
+ if ( isset( $args['highlight'] ) ) {
+ $lines = self::parseHighlightLines( $args['highlight'] );
+ if ( count( $lines ) ) {
+ $options['hl_lines'] = implode( ' ', $lines );
+ }
+ }
+
+ // Starting line number
+ if ( isset( $args['start'] ) && ctype_digit( $args['start'] ) ) {
+ $options['linenostart'] = (int)$args['start'];
+ }
+
+ if ( $inline ) {
+ $options['nowrap'] = 1;
+ }
+
+ $cache = ObjectCache::getMainWANInstance();
+ $error = null;
+ $output = $cache->getWithSetCallback(
+ $cache->makeGlobalKey( 'highlight', self::makeCacheKeyHash( $code, $lexer, $options ) ),
+ $cache::TTL_MONTH,
+ function ( $oldValue, &$ttl ) use ( $code, $lexer, $options, &$error ) {
+ $optionPairs = [];
+ foreach ( $options as $k => $v ) {
+ $optionPairs[] = "{$k}={$v}";
+ }
+ $result = Shell::command(
+ self::getPygmentizePath(),
+ '-l', $lexer,
+ '-f', 'html',
+ '-O', implode( ',', $optionPairs )
+ )
+ ->input( $code )
+ ->restrict( Shell::RESTRICT_DEFAULT | Shell::NO_NETWORK )
+ ->execute();
+
+ if ( $result->getExitCode() != 0 ) {
+ $ttl = WANObjectCache::TTL_UNCACHEABLE;
+ $error = $result->getStderr();
+ return null;
+ }
+
+ return $result->getStdout();
+ }
+ );
+
+ if ( $error !== null || $output === null ) {
+ $status->warning( 'syntaxhighlight-error-pygments-invocation-failure' );
+ wfWarn( 'Failed to invoke Pygments: ' . $error );
+ // Fall back to preformatted code without syntax highlighting
+ $output = self::plainCodeWrap( $code, $inline );
+ }
+
+ if ( $inline ) {
+ // We've already trimmed the input $code before highlighting,
+ // but pygment's standard out adds a line break afterwards,
+ // which would then be preserved in the paragraph that wraps this,
+ // and become visible as a space. Avoid that.
+ $output = trim( $output );
+ }
+
+ $status->value = $output;
+ return $status;
+ }
+
+ /**
+ * Construct a cache key for the results of a Pygments invocation.
+ *
+ * @param string $code Code to be highlighted.
+ * @param string $lexer Lexer name.
+ * @param array $options Options array.
+ * @return string Cache key.
+ */
+ private static function makeCacheKeyHash( $code, $lexer, $options ) {
+ $optionString = FormatJson::encode( $options, false, FormatJson::ALL_OK );
+ return md5( "{$code}|{$lexer}|{$optionString}|" . self::CACHE_VERSION );
+ }
+
+ /**
+ * Take an input specifying a list of lines to highlight, returning
+ * a raw list of matching line numbers.
+ *
+ * Input is comma-separated list of lines or line ranges.
+ *
+ * @param string $lineSpec
+ * @return int[] Line numbers.
+ */
+ protected static function parseHighlightLines( $lineSpec ) {
+ $lines = [];
+ $values = array_map( 'trim', explode( ',', $lineSpec ) );
+ foreach ( $values as $value ) {
+ if ( ctype_digit( $value ) ) {
+ $lines[] = (int)$value;
+ } elseif ( strpos( $value, '-' ) !== false ) {
+ list( $start, $end ) = array_map( 'trim', explode( '-', $value ) );
+ if ( self::validHighlightRange( $start, $end ) ) {
+ for ( $i = intval( $start ); $i <= $end; $i++ ) {
+ $lines[] = $i;
+ }
+ }
+ }
+ if ( count( $lines ) > self::HIGHLIGHT_MAX_LINES ) {
+ $lines = array_slice( $lines, 0, self::HIGHLIGHT_MAX_LINES );
+ break;
+ }
+ }
+ return $lines;
+ }
+
+ /**
+ * Validate a provided input range
+ * @param int $start
+ * @param int $end
+ * @return bool
+ */
+ protected static function validHighlightRange( $start, $end ) {
+ // Since we're taking this tiny range and producing a an
+ // array of every integer between them, it would be trivial
+ // to DoS the system by asking for a huge range.
+ // Impose an arbitrary limit on the number of lines in a
+ // given range to reduce the impact.
+ return ctype_digit( $start ) &&
+ ctype_digit( $end ) &&
+ $start > 0 &&
+ $start < $end &&
+ $end - $start < self::HIGHLIGHT_MAX_LINES;
+ }
+
+ /**
+ * Hook into Content::getParserOutput to provide syntax highlighting for
+ * script content.
+ *
+ * @param Content $content
+ * @param Title $title
+ * @param int $revId
+ * @param ParserOptions $options
+ * @param bool $generateHtml
+ * @param ParserOutput &$output
+ * @return bool
+ * @since MW 1.21
+ */
+ public static function onContentGetParserOutput( Content $content, Title $title,
+ $revId, ParserOptions $options, $generateHtml, ParserOutput &$output
+ ) {
+ global $wgParser, $wgTextModelsToParse;
+
+ if ( !$generateHtml ) {
+ // Nothing special for us to do, let MediaWiki handle this.
+ return true;
+ }
+
+ // Determine the language
+ $extension = ExtensionRegistry::getInstance();
+ $models = $extension->getAttribute( 'SyntaxHighlightModels' );
+ $model = $content->getModel();
+ if ( !isset( $models[$model] ) ) {
+ // We don't care about this model, carry on.
+ return true;
+ }
+ $lexer = $models[$model];
+
+ // Hope that $wgSyntaxHighlightModels does not contain silly types.
+ $text = ContentHandler::getContentText( $content );
+ if ( !$text ) {
+ // Oops! Non-text content? Let MediaWiki handle this.
+ return true;
+ }
+
+ // Parse using the standard parser to get links etc. into the database, HTML is replaced below.
+ // We could do this using $content->fillParserOutput(), but alas it is 'protected'.
+ if ( $content instanceof TextContent && in_array( $model, $wgTextModelsToParse ) ) {
+ $output = $wgParser->parse( $text, $title, $options, true, true, $revId );
+ }
+
+ $status = self::highlight( $text, $lexer );
+ if ( !$status->isOK() ) {
+ return true;
+ }
+ $out = $status->getValue();
+
+ $output->addModuleStyles( 'ext.pygments' );
+ $output->setText( '<div dir="ltr">' . $out . '</div>' );
+
+ // Inform MediaWiki that we have parsed this page and it shouldn't mess with it.
+ return false;
+ }
+
+ /**
+ * Hook to provide syntax highlighting for API pretty-printed output
+ *
+ * @param IContextSource $context
+ * @param string $text
+ * @param string $mime
+ * @param string $format
+ * @since MW 1.24
+ * @return bool
+ */
+ public static function onApiFormatHighlight( IContextSource $context, $text, $mime, $format ) {
+ if ( !isset( self::$mimeLexers[$mime] ) ) {
+ return true;
+ }
+
+ $lexer = self::$mimeLexers[$mime];
+ $status = self::highlight( $text, $lexer );
+ if ( !$status->isOK() ) {
+ return true;
+ }
+
+ $out = $status->getValue();
+ if ( preg_match( '/^<pre([^>]*)>/i', $out, $m ) ) {
+ $attrs = Sanitizer::decodeTagAttributes( $m[1] );
+ $attrs['class'] .= ' api-pretty-content';
+ $encodedAttrs = Sanitizer::safeEncodeTagAttributes( $attrs );
+ $out = '<pre' . $encodedAttrs. '>' . substr( $out, strlen( $m[0] ) );
+ }
+ $output = $context->getOutput();
+ $output->addModuleStyles( 'ext.pygments' );
+ $output->addHTML( '<div dir="ltr">' . $out . '</div>' );
+
+ // Inform MediaWiki that we have parsed this page and it shouldn't mess with it.
+ return false;
+ }
+
+ /**
+ * Conditionally register resource loader modules that depends on the
+ * VisualEditor MediaWiki extension.
+ *
+ * @param ResourceLoader &$resourceLoader
+ */
+ public static function onResourceLoaderRegisterModules( &$resourceLoader ) {
+ if ( !ExtensionRegistry::getInstance()->isLoaded( 'VisualEditor' ) ) {
+ return;
+ }
+
+ $resourceLoader->register( 'ext.geshi.visualEditor', [
+ 'class' => ResourceLoaderSyntaxHighlightVisualEditorModule::class,
+ 'localBasePath' => __DIR__ . '/../modules',
+ 'remoteExtPath' => 'SyntaxHighlight_GeSHi/modules',
+ 'scripts' => [
+ 've-syntaxhighlight/ve.dm.MWSyntaxHighlightNode.js',
+ 've-syntaxhighlight/ve.ce.MWSyntaxHighlightNode.js',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightWindow.js',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightDialog.js',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightDialogTool.js',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightInspector.js',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightInspectorTool.js',
+ ],
+ 'styles' => [
+ 've-syntaxhighlight/ve.ce.MWSyntaxHighlightNode.css',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightDialog.css',
+ 've-syntaxhighlight/ve.ui.MWSyntaxHighlightInspector.css',
+ ],
+ 'dependencies' => [
+ 'ext.visualEditor.mwcore',
+ 'oojs-ui.styles.icons-editing-advanced'
+ ],
+ 'messages' => [
+ 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-code',
+ 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-language',
+ 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-none',
+ 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-showlines',
+ 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-startingline',
+ 'syntaxhighlight-visualeditor-mwsyntaxhighlightinspector-title',
+ ],
+ 'targets' => [ 'desktop', 'mobile' ],
+ ] );
+ }
+
+ /**
+ * Backward-compatibility shim for extensions.
+ * @deprecated since MW 1.25
+ */
+ public static function prepare( $text, $lang ) {
+ wfDeprecated( __METHOD__ );
+ return new GeSHi( self::highlight( $text, $lang )->getValue() );
+ }
+
+ /**
+ * Backward-compatibility shim for extensions.
+ * @deprecated since MW 1.25
+ * @param GeSHi $geshi
+ * @return string
+ */
+ public static function buildHeadItem( $geshi ) {
+ wfDeprecated( __METHOD__ );
+ $geshi->parse_code();
+ return '';
+ }
+}
+class_alias( SyntaxHighlight::class, 'SyntaxHighlight_GeSHi' );
diff --git a/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightAce.php b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightAce.php
new file mode 100644
index 00000000..54991f32
--- /dev/null
+++ b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightAce.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/**
+ * Class provides functionality to map Ace lexer definitions
+ */
+class SyntaxHighlightAce {
+ /** @var array This map is inverted, because it is easier to maintain this way */
+ private static $aceLexers = [
+ 'ABAP' => [ 'abap' ],
+ 'ABC' => [],
+ 'ActionScript' => [ 'actionscript', 'actionscript3' ],
+ 'ADA' => [ 'ada', 'ada2005', 'ada95' ],
+ 'Apache_Conf' => [ 'apache', 'apacheconf', 'aconf' ],
+ 'AsciiDoc' => [],
+ 'Assembly_x86' => [ 'nasm' ],
+ 'AutoHotKey' => [ 'autohotkey', 'ah' ],
+ 'BatchFile' => [ 'bat', 'batch', 'dosbatch', 'winbatch' ],
+ 'C_Cpp' => [ 'cpp', 'c++' ],
+ 'C9Search' => [],
+ 'Cirru' => [ 'cirru' ],
+ 'Clojure' => [ 'clojure', 'clj' ],
+ 'Cobol' => [ 'cobol' ],
+ 'coffee' => [ 'coffee', 'coffeescript', 'coffee-script' ],
+ 'ColdFusion' => [ 'cfm' ],
+ 'CSharp' => [ 'csharp', '#' ],
+ 'CSS' => [ 'css' ],
+ 'Curly' => [],
+ 'D' => [ 'd' ],
+ 'Dart' => [ 'dart' ],
+ 'Diff' => [ 'diff', 'udiff' ],
+ 'Django' => [ 'django', 'html+django', 'html+jinja', 'htmldjango' ],
+ 'Dockerfile' => [ 'Dockerfile', 'docker' ],
+ 'Dot' => [],
+ 'Dummy' => [],
+ 'DummySyntax' => [],
+ 'Eiffel' => [ 'eiffel' ],
+ 'EJS' => [],
+ 'Elixir' => [ 'elixer', 'ex', 'exs' ],
+ 'Elm' => [ 'elm' ],
+ 'Erlang' => [ 'erlang' ],
+ 'Forth' => [],
+ 'Fortran' => [ 'fortran' ],
+ 'FTL' => [],
+ 'Gcode' => [],
+ 'Gherkin' => [ 'cucumber', 'gherkin' ],
+ 'Gitignore' => [],
+ 'Glsl' => [ 'glsl' ],
+ 'Gobstones' => [],
+ 'golang' => [ 'go' ],
+ 'Groovy' => [ 'groovy' ],
+ 'HAML' => [ 'haml' ],
+ 'Handlebars' => [ 'html+handlebars' ],
+ 'Haskell' => [ 'haskell', 'hs' ],
+ 'haXe' => [ 'hx', 'haxe', 'hxsl' ],
+ 'HTML' => [ 'html' ],
+ 'HTML_Elixir' => [],
+ 'HTML_Ruby' => [ 'rhtml', 'html+erb', 'html+ruby' ],
+ 'INI' => [ 'ini', 'cfg', 'dosini' ],
+ 'Io' => [ 'io' ],
+ 'Jack' => [ '' ],
+ 'Jade' => [ 'jade' ],
+ 'Java' => [ 'java' ],
+ 'JavaScript' => [ 'Javascript', 'js' ],
+ 'JSON' => [ 'json' ],
+ 'JSONiq' => [],
+ 'JSP' => [ 'jsp' ],
+ 'JSX' => [],
+ 'Julia' => [ 'julia', 'jl' ],
+ 'LaTeX' => [ 'latex' ],
+ 'Lean' => [ 'lean' ],
+ 'LESS' => [ 'less' ],
+ 'Liquid' => [ 'liquid' ],
+ 'Lisp' => [ 'lisp', 'common-lisp', 'cl' ],
+ 'LiveScript' => [ 'Livescript', 'live-script' ],
+ 'LogiQL' => [],
+ 'LSL' => [ 'lsl' ],
+ 'Lua' => [ 'lua' ],
+ 'LuaPage' => [],
+ 'Lucene' => [],
+ 'Makefile' => [ 'make', 'makefile', 'mf', 'bsdmake' ],
+ 'Markdown' => [],
+ 'Mask' => [ 'mask' ],
+ 'MATLAB' => [ 'matlab' ],
+ 'Maze' => [],
+ 'MEL' => [],
+ 'MUSHCode' => [],
+ 'MySQL' => [ 'mysql' ],
+ 'Nix' => [ 'nix', 'nixos' ],
+ 'NSIS' => [ 'nsis', 'nsi', 'nsh' ],
+ 'ObjectiveC' => [ 'objectivec', 'objective-c', 'obj-c', 'objc',
+ 'objective-c++', 'objectivec++', 'obj-c++', 'objc++' ],
+ 'OCaml' => [ 'ocaml' ],
+ 'Pascal' => [ 'pascal', 'delphi', 'pas', 'objectpascal' ],
+ 'Perl' => [ 'perl', 'pl', 'perl6', 'pl6' ],
+ 'pgSQL' => [ 'postgresql', 'postgres' ],
+ 'PHP' => [ 'php', 'php3', 'php4', 'php5', 'html+php' ],
+ 'Powershell' => [ 'powershell', 'posh', 'ps1', 'psm1' ],
+ 'Praat' => [ 'praat' ],
+ 'Prolog' => [ 'prolog' ],
+ 'Properties' => [ 'properties', 'jproperties' ],
+ 'Protobuf' => [ 'protobuf', 'proto' ],
+ 'Python' => [ 'python', 'py', 'sage', 'pyton3', 'py3' ],
+ 'R' => [],
+ 'Razor' => [],
+ 'RDoc' => [],
+ 'RHTML' => [], // HTML with Rcode, not ruby
+ 'RST' => [ 'rst', 'rest', 'restructuredtext' ],
+ 'Ruby' => [ 'ruby', 'rb', 'duby' ],
+ 'Rust' => [ 'rust' ],
+ 'SASS' => [ 'sass' ],
+ 'SCAD' => [],
+ 'Scala' => [ 'scala' ],
+ 'Scheme' => [ 'scheme', 'scm' ],
+ 'SCSS' => [ 'scss' ],
+ 'SH' => [ 'sh', 'bash', 'ksh', 'shell' ],
+ 'SJS' => [],
+ 'Smarty' => [ 'smarty', 'html+smarty' ],
+ 'snippets' => [],
+ 'Soy_Template' => [],
+ 'Space' => [],
+ 'SQL' => [ 'sql' ],
+ 'SQLServer' => [],
+ 'Stylus' => [],
+ 'SVG' => [],
+ 'Swift' => [ 'swift' ],
+ 'Tcl' => [ 'tcl' ],
+ 'Tex' => [ 'tex' ],
+ 'Text' => [ 'text' ],
+ 'Textile' => [],
+ 'Toml' => [],
+ 'Twig' => [ 'html+twig', 'twig' ],
+ 'Typescript' => [ 'typescript', 'ts' ],
+ 'Vala' => [ 'vala', 'vapi' ],
+ 'VBScript' => [],
+ 'Velocity' => [ 'velocity', 'html+velocity' ],
+ 'Verilog' => [ 'verilog', 'v', 'systemverilog', 'sv' ],
+ 'VHDL' => [ 'vhdl' ],
+ 'Wollok' => [],
+ 'XML' => [ 'xml' ],
+ 'XQuery' => [ 'xquery', 'xqy', 'xq', 'xql', 'xqm' ],
+ 'YAML' => [ 'yaml' ],
+ ];
+
+ public static function getPygmentsToAceMap() {
+ $result = [];
+ foreach ( self::$aceLexers as $aceName => $pygmentsLexers ) {
+ foreach ( $pygmentsLexers as $lexer ) {
+ if ( strcasecmp( $lexer, $aceName ) === 0 ) {
+ continue;
+ }
+ if ( !array_key_exists( $lexer, $result ) ) {
+ $result[ $lexer ] = $aceName;
+ }
+ }
+ }
+ return $result;
+ }
+}
diff --git a/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightGeSHiCompat.php b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightGeSHiCompat.php
new file mode 100644
index 00000000..a6f38323
--- /dev/null
+++ b/www/wiki/extensions/SyntaxHighlight_GeSHi/includes/SyntaxHighlightGeSHiCompat.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+class SyntaxHighlightGeSHiCompat {
+ /** @var array A mapping of GeSHi lexer names to compatible Pygments lexers. */
+ private static $compatibleLexers = [
+ // Assembler
+ 'arm' => 'asm',
+ '6502acme' => 'asm',
+ '6502tasm' => 'asm',
+ '6502kickass' => 'asm',
+ '68000devpac' => 'asm',
+ 'dcpu16' => 'asm',
+ 'm68k' => 'asm',
+ 'mmix' => 'nasm',
+ 'mpasm' => 'asm',
+ 'pic16' => 'asm',
+ 'z80' => 'asm',
+
+ // BASIC
+ 'xbasic' => 'basic',
+ 'thinbasic' => 'basic',
+ 'sdlbasic' => 'basic',
+ 'purebasic' => 'basic',
+ 'mapbasic' => 'basic',
+ 'locobasic' => 'basic',
+ 'gwbasic' => 'basic',
+ 'freebasic' => 'basic',
+ 'basic4gl' => 'basic',
+ 'zxbasic' => 'basic',
+ 'gambas' => 'basic',
+ 'oobas' => 'basic',
+ 'bascomavr' => 'basic',
+
+ // C / C++
+ 'c_loadrunner' => 'c',
+ 'c_mac' => 'c',
+ 'c_winapi' => 'c',
+ 'upc' => 'c',
+ 'cpp-qt' => 'cpp',
+ 'cpp-winapi' => 'cpp',
+ 'urbi' => 'cpp',
+
+ // HTML
+ 'html4strict' => 'html',
+ 'html5' => 'html',
+
+ // JavaScript
+ 'jquery' => 'javascript',
+ 'ecmascript' => 'javascript',
+
+ // Microsoft
+ 'vb' => 'vbnet',
+ 'asp' => 'aspx-vb',
+ 'visualfoxpro' => 'foxpro',
+ 'dos' => 'bat',
+ 'visualprolog' => 'prolog',
+ 'reg' => 'registry',
+
+ // Miscellaneous
+ 'cadlisp' => 'lisp',
+ 'java5' => 'java',
+ 'php-brief' => 'php',
+ 'povray' => 'pov',
+ 'pys60' => 'python',
+ 'rails' => 'ruby',
+ 'rpmspec' => 'spec',
+ 'rsplus' => 'splus',
+ 'gettext' => 'pot',
+
+ // ML
+ 'ocaml-brief' => 'ocaml',
+ 'standardml' => 'sml',
+
+ // Modula 2
+ 'modula3' => 'modula2',
+ 'oberon2' => 'modula2',
+
+ // SQL
+ 'dcl' => 'sql',
+ 'plsql' => 'sql',
+ 'oracle11' => 'sql',
+ 'oracle8' => 'sql',
+
+ // REXX
+ 'oorexx' => 'rexx',
+ 'netrexx' => 'rexx',
+
+ // xpp is basically Java
+ 'xpp' => 'java',
+
+ // apt
+ 'apt_sources' => 'debsources',
+ ];
+
+ public static function getGeSHiToPygmentsMap() {
+ return self::$compatibleLexers;
+ }
+}