codemirror.mediawiki.js: add Mod-Shift-x

This patch adds a keyboard shortcut `Mod-Shift-x` to toggle between left-to-right (LTR) and right-to-left (RTL) text directions.

Bug: T170001
Change-Id: Ia857ad0b0aff0bb206b45e4d27dee6e91a3effce
This commit is contained in:
bhsd 2024-06-08 11:10:11 +08:00
parent 999382fd16
commit 0e0e4927ab
7 changed files with 66 additions and 31 deletions

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
"use strict";var e=require("ext.CodeMirror.v6"),i=require("ext.CodeMirror.v6.mode.mediawiki");require("ext.CodeMirror.v6.lib");var r=document.getElementById("wpTextbox1"),o=new e(r),t=new URLSearchParams(window.location.search);o.initialize([o.defaultExtensions,i({bidiIsolation:"rtl"===r.dir&&t.get("cm6bidi")})]);
"use strict";var e=require("ext.CodeMirror.v6"),i=require("ext.CodeMirror.v6.mode.mediawiki");require("ext.CodeMirror.v6.lib");var r=new e(document.getElementById("wpTextbox1")),o=new URLSearchParams(window.location.search);r.initialize([r.defaultExtensions,i({bidiIsolation:o.get("cm6bidi")})]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -28,29 +28,31 @@ const isolate = Decoration.mark( {
function computeIsolates( view ) {
const set = new RangeSetBuilder();
for ( const { from, to } of view.visibleRanges ) {
let startPos = null;
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 ( view.editorAttrs.dir === 'rtl' ) {
for ( const { from, to } of view.visibleRanges ) {
let startPos = null;
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 === null && 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;
if ( startPos === null && 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();
@ -69,6 +71,8 @@ class CodeMirrorBidiIsolation {
this.isolates = computeIsolates( view );
/** @type {Tree} */
this.tree = syntaxTree( view.state );
/** @type {Direction} */
this.dir = view.textDirection;
}
/**
@ -76,7 +80,8 @@ class CodeMirrorBidiIsolation {
*/
update( update ) {
if ( update.docChanged || update.viewportChanged ||
syntaxTree( update.state ) !== this.tree
syntaxTree( update.state ) !== this.tree ||
update.view.textDirection !== this.dir
) {
this.isolates = computeIsolates( update.view );
this.tree = syntaxTree( update.state );

View file

@ -1,4 +1,4 @@
import { EditorState, Extension } from '@codemirror/state';
import { EditorState, Extension, Compartment } from '@codemirror/state';
import {
EditorView,
drawSelection,
@ -6,7 +6,8 @@ import {
highlightSpecialChars,
keymap,
rectangularSelection,
crosshairCursor
crosshairCursor,
ViewUpdate
} from '@codemirror/view';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap } from '@codemirror/search';
@ -73,6 +74,12 @@ class CodeMirror {
* @type {CodeMirrorTextSelection}
*/
this.textSelection = null;
/**
* Language direction extension.
*
* @type {Compartment}
*/
this.dirCompartment = new Compartment();
}
/**
@ -91,6 +98,7 @@ class CodeMirror {
this.heightExtension,
this.updateExtension,
this.bracketMatchingExtension,
this.dirExtension,
EditorState.readOnly.of( this.readOnly ),
EditorView.domEventHandlers( {
blur: () => this.$textarea.triggerHandler( 'blur' ),
@ -221,9 +229,8 @@ class CodeMirror {
} ),
// .cm-editor element (contains the whole CodeMirror UI)
EditorView.editorAttributes.of( {
// Use direction and language of the original textbox.
// Use language of the original textbox.
// These should be attributes of .cm-editor, not the .cm-content (T359589)
dir: this.$textarea.attr( 'dir' ),
lang: this.$textarea.attr( 'lang' )
} ),
// The search panel should use the same direction as the interface language (T359611)
@ -321,6 +328,29 @@ class CodeMirror {
} );
}
get dirExtension() {
return [
this.dirCompartment.of( EditorView.editorAttributes.of( {
// Use direction of the original textbox.
// These should be attributes of .cm-editor, not the .cm-content (T359589)
dir: this.$textarea.attr( 'dir' )
} ) ),
keymap.of( [ {
key: 'Mod-Shift-x',
run: ( view ) => {
const dir = this.$textarea.attr( 'dir' ) === 'rtl' ? 'ltr' : 'rtl';
this.$textarea.attr( 'dir', dir );
view.dispatch( {
effects: this.dirCompartment.reconfigure(
EditorView.editorAttributes.of( { dir } )
)
} );
return true;
}
} ] )
];
}
/**
* Setup CodeMirror and add it to the DOM. This will hide the original textarea.
*

View file

@ -8,6 +8,6 @@ const urlParams = new URLSearchParams( window.location.search );
cm.initialize( [
cm.defaultExtensions,
mediaWikiLang( {
bidiIsolation: textarea.dir === 'rtl' && urlParams.get( 'cm6bidi' )
bidiIsolation: urlParams.get( 'cm6bidi' )
} )
] );