mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/CodeMirror
synced 2024-12-19 01:00:36 +00:00
d652f3d2a2
There is a known bug with JSDoc and using `export default`. These must be separate statements for JSDoc to parse properly. See https://github.com/jsdoc/jsdoc/issues/1132 Update README; change log now lives on the wiki. Bug: T359986 Depends-On: I58a0766e35eddaf7bebe2c080757bb09963d8555 Change-Id: Ibc2212ef9eab512511b13a99ecc2ccbda8c52ece
123 lines
2.8 KiB
JavaScript
123 lines
2.8 KiB
JavaScript
import {
|
|
Decoration,
|
|
DecorationSet,
|
|
Direction,
|
|
EditorView,
|
|
PluginSpec,
|
|
ViewPlugin,
|
|
ViewUpdate
|
|
} from '@codemirror/view';
|
|
import { Prec, RangeSet, RangeSetBuilder } from '@codemirror/state';
|
|
import { syntaxTree } from '@codemirror/language';
|
|
import { mwModeConfig } from './codemirror.mode.mediawiki.config';
|
|
|
|
/**
|
|
* @type {Decoration}
|
|
* @private
|
|
*/
|
|
const isolate = Decoration.mark( {
|
|
class: 'cm-bidi-isolate',
|
|
bidiIsolate: Direction.LTR
|
|
} );
|
|
|
|
/**
|
|
* @param {EditorView} view
|
|
* @return {RangeSet}
|
|
* @private
|
|
*/
|
|
function computeIsolates( view ) {
|
|
const set = new RangeSetBuilder();
|
|
|
|
for ( const { from, to } of view.visibleRanges ) {
|
|
let startPos;
|
|
syntaxTree( view.state ).iterate( {
|
|
from,
|
|
to,
|
|
enter( node ) {
|
|
// Determine if this is a bracket node (start or end of a tag).
|
|
const isBracket = node.name.split( '_' )
|
|
.some( ( tag ) => [
|
|
mwModeConfig.tags.htmlTagBracket,
|
|
mwModeConfig.tags.extTagBracket
|
|
].includes( tag ) );
|
|
|
|
if ( !startPos && isBracket ) {
|
|
// If we find a bracket node, we keep track of the start position.
|
|
startPos = node.from;
|
|
} else if ( isBracket ) {
|
|
// When we find the closing bracket, add the isolate.
|
|
set.add( startPos, node.to, isolate );
|
|
startPos = null;
|
|
}
|
|
}
|
|
} );
|
|
}
|
|
|
|
return set.finish();
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
class CodeMirrorBidiIsolation {
|
|
/**
|
|
* @constructor
|
|
* @param {EditorView} view The editor view.
|
|
*/
|
|
constructor( view ) {
|
|
/** @type {DecorationSet} */
|
|
this.isolates = computeIsolates( view );
|
|
/** @type {Tree} */
|
|
this.tree = syntaxTree( view.state );
|
|
}
|
|
|
|
/**
|
|
* @param {ViewUpdate} update
|
|
*/
|
|
update( update ) {
|
|
if ( update.docChanged || update.viewportChanged ||
|
|
syntaxTree( update.state ) !== this.tree
|
|
) {
|
|
this.isolates = computeIsolates( update.view );
|
|
this.tree = syntaxTree( update.state );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @type {PluginSpec}
|
|
* @private
|
|
*/
|
|
const bidiIsolationSpec = {
|
|
provide: ( plugin ) => {
|
|
/**
|
|
* @param {EditorView} view
|
|
* @return {DecorationSet}
|
|
*/
|
|
const access = ( view ) => {
|
|
return view.plugin( plugin ) ?
|
|
( view.plugin( plugin ).isolates || Decoration.none ) :
|
|
Decoration.none;
|
|
};
|
|
|
|
// Use the lowest precedence to ensure that other decorations
|
|
// don't break up the isolating decorations.
|
|
return Prec.lowest( [
|
|
EditorView.decorations.of( access ),
|
|
EditorView.bidiIsolatedRanges.of( access )
|
|
] );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Bidirectional isolation plugin for CodeMirror for use on RTL pages.
|
|
* This ensures HTML and MediaWiki tags are always displayed left-to-right.
|
|
*
|
|
* Use this plugin by passing in `bidiIsolation: true` when instantiating
|
|
* a [CodeMirrorModeMediaWiki]{@link CodeMirrorModeMediaWiki} object.
|
|
*
|
|
* @module CodeMirrorBidiIsolation
|
|
* @see https://codemirror.net/examples/bidi/
|
|
*/
|
|
export default ViewPlugin.fromClass( CodeMirrorBidiIsolation, bidiIsolationSpec );
|