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,6 +28,7 @@ const isolate = Decoration.mark( {
function computeIsolates( view ) { function computeIsolates( view ) {
const set = new RangeSetBuilder(); const set = new RangeSetBuilder();
if ( view.editorAttrs.dir === 'rtl' ) {
for ( const { from, to } of view.visibleRanges ) { for ( const { from, to } of view.visibleRanges ) {
let startPos = null; let startPos = null;
syntaxTree( view.state ).iterate( { syntaxTree( view.state ).iterate( {
@ -52,6 +53,7 @@ function computeIsolates( view ) {
} }
} ); } );
} }
}
return set.finish(); return set.finish();
} }
@ -69,6 +71,8 @@ class CodeMirrorBidiIsolation {
this.isolates = computeIsolates( view ); this.isolates = computeIsolates( view );
/** @type {Tree} */ /** @type {Tree} */
this.tree = syntaxTree( view.state ); this.tree = syntaxTree( view.state );
/** @type {Direction} */
this.dir = view.textDirection;
} }
/** /**
@ -76,7 +80,8 @@ class CodeMirrorBidiIsolation {
*/ */
update( update ) { update( update ) {
if ( update.docChanged || update.viewportChanged || 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.isolates = computeIsolates( update.view );
this.tree = syntaxTree( update.state ); 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 { import {
EditorView, EditorView,
drawSelection, drawSelection,
@ -6,7 +6,8 @@ import {
highlightSpecialChars, highlightSpecialChars,
keymap, keymap,
rectangularSelection, rectangularSelection,
crosshairCursor crosshairCursor,
ViewUpdate
} from '@codemirror/view'; } from '@codemirror/view';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap } from '@codemirror/search'; import { searchKeymap } from '@codemirror/search';
@ -73,6 +74,12 @@ class CodeMirror {
* @type {CodeMirrorTextSelection} * @type {CodeMirrorTextSelection}
*/ */
this.textSelection = null; this.textSelection = null;
/**
* Language direction extension.
*
* @type {Compartment}
*/
this.dirCompartment = new Compartment();
} }
/** /**
@ -91,6 +98,7 @@ class CodeMirror {
this.heightExtension, this.heightExtension,
this.updateExtension, this.updateExtension,
this.bracketMatchingExtension, this.bracketMatchingExtension,
this.dirExtension,
EditorState.readOnly.of( this.readOnly ), EditorState.readOnly.of( this.readOnly ),
EditorView.domEventHandlers( { EditorView.domEventHandlers( {
blur: () => this.$textarea.triggerHandler( 'blur' ), blur: () => this.$textarea.triggerHandler( 'blur' ),
@ -221,9 +229,8 @@ class CodeMirror {
} ), } ),
// .cm-editor element (contains the whole CodeMirror UI) // .cm-editor element (contains the whole CodeMirror UI)
EditorView.editorAttributes.of( { 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) // These should be attributes of .cm-editor, not the .cm-content (T359589)
dir: this.$textarea.attr( 'dir' ),
lang: this.$textarea.attr( 'lang' ) lang: this.$textarea.attr( 'lang' )
} ), } ),
// The search panel should use the same direction as the interface language (T359611) // 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. * 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.initialize( [
cm.defaultExtensions, cm.defaultExtensions,
mediaWikiLang( { mediaWikiLang( {
bidiIsolation: textarea.dir === 'rtl' && urlParams.get( 'cm6bidi' ) bidiIsolation: urlParams.get( 'cm6bidi' )
} ) } )
] ); ] );