CM6: move bidiIsolation to be part of CodeMirrorModeMediaWiki

The extension is custom built for MediaWiki (i.e. handling of extension
tags like <ref> that aren't HTML tags), so it only makes sense to bundle
it as part of the MediaWiki language mode.

Resultantly, we can no longer check the direction of the textarea where
we enable bidi isolation, because the language mode should have no
knowledge of the textarea. Instead we offer a `config` object (akin to
other language modes offered by CodeMirror), with currently only one
option: `bidiIsolation`. It is the responsibility of the caller to
enable this where desired.

Also make templateFolding and CodeMirrorModeMediaWiki use
`export default` since they both only export one thing.

This commit is in preparation for Ide716247e5, where we need bidi
isolation separated from the CodeMirror class due to its dependency on
CodeMirrorModeMediaConfig.

Bug: T358804
Bug: T214989
Change-Id: If3211bd259bd7833919a627faabd86ae7aa81b53
This commit is contained in:
MusikAnimal 2024-03-14 14:32:14 -04:00
parent b18ded0a13
commit 00f947e97f
8 changed files with 35 additions and 23 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

@ -4,7 +4,6 @@ import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap } from '@codemirror/search'; import { searchKeymap } from '@codemirror/search';
import { bracketMatching } from '@codemirror/language'; import { bracketMatching } from '@codemirror/language';
import CodemirrorTextSelection from './codemirror.textSelection'; import CodemirrorTextSelection from './codemirror.textSelection';
import bidiIsolationExtension from './codemirror.bidiIsolation';
// Necessary so that `require` doesn't get mangled into `__webpack_require__`, // Necessary so that `require` doesn't get mangled into `__webpack_require__`,
// which ResourceLoader won't recognize and thus be unable to load the virtual file. // which ResourceLoader won't recognize and thus be unable to load the virtual file.
@ -68,11 +67,6 @@ export default class CodeMirror {
extensions.push( keymap.of( historyKeymap ) ); extensions.push( keymap.of( historyKeymap ) );
} }
// Add bidi isolation to tags on RTL pages (T358804).
if ( this.$textarea.attr( 'dir' ) === 'rtl' ) {
extensions.push( bidiIsolationExtension );
}
// Set to [] to disable everywhere, or null to enable everywhere // Set to [] to disable everywhere, or null to enable everywhere
const namespaces = mw.config.get( 'extCodeMirrorConfig' ).lineNumberingNamespaces; const namespaces = mw.config.get( 'extCodeMirrorConfig' ).lineNumberingNamespaces;
if ( !namespaces || namespaces.includes( mw.config.get( 'wgNamespaceNumber' ) ) ) { if ( !namespaces || namespaces.includes( mw.config.get( 'wgNamespaceNumber' ) ) ) {

View file

@ -8,7 +8,8 @@ import {
} from '@codemirror/language'; } from '@codemirror/language';
import { mwModeConfig as modeConfig } from './codemirror.mode.mediawiki.config'; import { mwModeConfig as modeConfig } from './codemirror.mode.mediawiki.config';
import { Tag } from '@lezer/highlight'; import { Tag } from '@lezer/highlight';
import { templateFoldingExtension } from './codemirror.templateFolding'; import templateFoldingExtension from './codemirror.templateFolding';
import bidiIsolationExtension from './codemirror.bidiIsolation';
/** /**
* Adapted from the original CodeMirror 5 stream parser by Pavel Astakhov * Adapted from the original CodeMirror 5 stream parser by Pavel Astakhov
@ -1211,6 +1212,12 @@ class CodeMirrorModeMediaWiki {
} }
} }
/**
* @typedef {Object} mediaWikiLangConfig
* @property {boolean} [bidiIsolation=false] Enable bidi isolation around HTML tags.
* This should generally always be enabled on RTL pages, but it comes with a performance cost.
*/
/** /**
* Gets a LanguageSupport instance for the MediaWiki mode. * Gets a LanguageSupport instance for the MediaWiki mode.
* *
@ -1220,12 +1227,13 @@ class CodeMirrorModeMediaWiki {
* const cm = new CodeMirror( textarea ); * const cm = new CodeMirror( textarea );
* cm.initialize( [ ...cm.defaultExtensions, mediaWikiLang() ] ); * cm.initialize( [ ...cm.defaultExtensions, mediaWikiLang() ] );
* *
* @param {Object|null} [config] Used only by unit tests. * @param {mediaWikiLangConfig} [config] Configuration options for the MediaWiki mode.
* @param {Object|null} [mwConfig] Ignore; used only by unit tests.
* @return {LanguageSupport} * @return {LanguageSupport}
*/ */
export const mediaWikiLang = ( config = null ) => { export default ( config = { bidiIsolation: false }, mwConfig = null ) => {
config = config || mw.config.get( 'extCodeMirrorConfig' ); mwConfig = mwConfig || mw.config.get( 'extCodeMirrorConfig' );
const mode = new CodeMirrorModeMediaWiki( config ); const mode = new CodeMirrorModeMediaWiki( mwConfig );
const parser = mode.mediawiki; const parser = mode.mediawiki;
const lang = StreamLanguage.define( parser ); const lang = StreamLanguage.define( parser );
const langExtension = [ syntaxHighlighting( const langExtension = [ syntaxHighlighting(
@ -1235,11 +1243,17 @@ export const mediaWikiLang = ( config = null ) => {
) ]; ) ];
// Add template folding if in supported namespace. // Add template folding if in supported namespace.
const templateFoldingNs = config.templateFoldingNamespaces; const templateFoldingNs = mwConfig.templateFoldingNamespaces;
// Set to [] to disable everywhere, or null to enable everywhere. // Set to [] to disable everywhere, or null to enable everywhere.
if ( !templateFoldingNs || templateFoldingNs.includes( mw.config.get( 'wgNamespaceNumber' ) ) ) { if ( !templateFoldingNs || templateFoldingNs.includes( mw.config.get( 'wgNamespaceNumber' ) ) ) {
langExtension.push( templateFoldingExtension ); langExtension.push( templateFoldingExtension );
} }
// Bundle the bidi isolation extension, as it's coded specifically for MediaWiki.
// This is behind a config option for performance reasons (we only use it on RTL pages).
if ( config.bidiIsolation ) {
langExtension.push( bidiIsolationExtension );
}
return new LanguageSupport( lang, langExtension ); return new LanguageSupport( lang, langExtension );
}; };

View file

@ -222,7 +222,7 @@ const foldKeymap = [
]; ];
/** @type {Extension} */ /** @type {Extension} */
export const templateFoldingExtension = [ export default [
codeFolding( { codeFolding( {
placeholderDOM( view ) { placeholderDOM( view ) {
const element = document.createElement( 'span' ); const element = document.createElement( 'span' );

View file

@ -1,9 +1,12 @@
import CodeMirrorWikiEditor from './codemirror.wikieditor'; import CodeMirrorWikiEditor from './codemirror.wikieditor';
import { mediaWikiLang } from './codemirror.mode.mediawiki'; import mediaWikiLang from './codemirror.mode.mediawiki';
if ( mw.loader.getState( 'ext.wikiEditor' ) ) { if ( mw.loader.getState( 'ext.wikiEditor' ) ) {
mw.hook( 'wikiEditor.toolbarReady' ).add( ( $textarea ) => { mw.hook( 'wikiEditor.toolbarReady' ).add( ( $textarea ) => {
const cmWE = new CodeMirrorWikiEditor( $textarea, mediaWikiLang() ); const cmWE = new CodeMirrorWikiEditor(
$textarea,
mediaWikiLang( { bidiIsolation: $textarea.attr( 'dir' ) === 'rtl' } )
);
cmWE.addCodeMirrorToWikiEditor(); cmWE.addCodeMirrorToWikiEditor();
} ); } );
} }

View file

@ -1,5 +1,5 @@
import CodeMirror from '../../src/codemirror.js'; import CodeMirror from '../../src/codemirror.js';
import { mediaWikiLang } from '../../src/codemirror.mode.mediawiki.js'; import mediaWikiLang from '../../src/codemirror.mode.mediawiki.js';
const testCases = [ const testCases = [
{ {
@ -14,9 +14,10 @@ const textarea = document.createElement( 'textarea' );
textarea.dir = 'rtl'; textarea.dir = 'rtl';
document.body.appendChild( textarea ); document.body.appendChild( textarea );
const cm = new CodeMirror( textarea ); const cm = new CodeMirror( textarea );
const mwLang = mediaWikiLang( { const mwLang = mediaWikiLang(
tags: {} { bidiIsolation: true },
} ); { tags: {} }
);
cm.initialize( [ ...cm.defaultExtensions, mwLang ] ); cm.initialize( [ ...cm.defaultExtensions, mwLang ] );
describe( 'CodeMirrorBidiIsolation', () => { describe( 'CodeMirrorBidiIsolation', () => {

View file

@ -1,5 +1,5 @@
import CodeMirror from '../../src/codemirror.js'; import CodeMirror from '../../src/codemirror.js';
import { mediaWikiLang } from '../../src/codemirror.mode.mediawiki.js'; import mediaWikiLang from '../../src/codemirror.mode.mediawiki.js';
import { mwModeConfig } from '../../src/codemirror.mode.mediawiki.config.js'; import { mwModeConfig } from '../../src/codemirror.mode.mediawiki.config.js';
// NOTE: each test case should have a space before the closing </div> // NOTE: each test case should have a space before the closing </div>
@ -163,7 +163,7 @@ const textarea = document.createElement( 'textarea' );
document.body.appendChild( textarea ); document.body.appendChild( textarea );
const cm = new CodeMirror( textarea ); const cm = new CodeMirror( textarea );
// Stub the config normally provided by mw.config.get('extCodeMirrorConfig') // Stub the config normally provided by mw.config.get('extCodeMirrorConfig')
const mwLang = mediaWikiLang( { const mwLang = mediaWikiLang( {}, {
urlProtocols: 'ftp://|https://|news:', urlProtocols: 'ftp://|https://|news:',
doubleUnderscore: [ { doubleUnderscore: [ {
__notoc__: 'notoc' __notoc__: 'notoc'