mediawiki-extensions-CodeMi.../src/codemirror.bidiIsolation.js
MusikAnimal d652f3d2a2 CM6: Add jsdoc build step, fix JSDoc annotations, and add @stable tags
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
2024-03-26 13:35:47 -04:00

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 );