Hello codemirror society!
First of all, I really appreciate your great work on codemirror!
I encountered an issue while using codemirror in editing Arabic documents. If the text editor direction is RTL but the line ends with text that is LTR, you can’t type any thing after that LTR text! Here is an illustration of what I mean from your RTL example:
As you see, I typed 10
at the end of the line. I can’t type a space after the 10
since codemirror will insert the space BEFORE the 10
. I can’t also break the line since codemirror will insert the line break BEFORE 10
too.
Thanks for attention. Best regards.
Editing on direction boundaries is messy, but I don’t think this is really a bug. It matches Firefox’s native editing behavior in this situation (though Chrome indeed behaves differently). You can put your cursor at the end (right) of the number to insert right-to-left text after it. In fact, just continuing to type after you type the number seems to work fine. It’s only after you move the start (left) of the number that inserted text will go before it.
Thanks for clarification! However, I’m used to the way Libreoffice ans MS word implement cursor movement. In both programs, the cursor moves logically, and not visually (e.g. left arrow in RTL text moves cursor to next character, whether it’s on the left or right). Is there a way to configure codemirror cursor to behave the same way?
Yes, you should be able to bind ArrowLeft
to cursorCharBackward
and ArrowRight
to cursorCharForward
instead of the defaults (cursorCharLeft
/cursorCharRight
).
Thank you! I’ll try to implement it.
I tried making my keymap, but the cursor still moves visually, here is my implementation:
const LogicalKeyMap = [
{key: "ArrowLeft", run: cursorCharForward, shift: selectCharForward, preventDefault: true},
{key: "Mod-ArrowLeft", mac: "Alt-ArrowLeft", run: cursorGroupForward, shift: selectGroupForward, preventDefault: true},
{key: "ArrowRight", run: cursorCharBackward, shift: selectCharBackward, preventDefault: true},
{key: "Mod-ArrowRight", mac: "Alt-ArrowRight", run: cursorGroupBackward, shift: selectGroupBackward, preventDefault: true},
I also tried calling cursorCharForward
every second, to see how the cursor will move:
m = function(){cursorCharForward(Editor); setTimeout(m,1000)}
m()
Here is the result:
Am I doing something wrong?
After looking at the source code, all what cursorCharForward
does is moving the cursor visually away from the line start.
You’re right! I thought I had already implemented such commands, but I hadn’t. This patch adds them.
Great! I also suggest adding cursorLogicalLeft
and cursorLogicalRight
. What cursorLogicalLeft
does for example is moving the cursor logically forward if the editor is RTL, but logically backward if the editor is LTR. cursorLogicalRight
does the opposite. This is my unprofessional implementation I’ve made recently:
function cursorCharForwardLogical(editor, range){
let cursor_position;
if(range.from == range.to){
cursor_position = EditorSelection.create([EditorSelection.range(
Math.min(range.from + 1, editor.state.doc.length),
Math.min(range.from + 1, editor.state.doc.length)
)]);
}
else{
cursor_position = EditorSelection.create([EditorSelection.range(
range.to,
range.to
)]);
}
const mutation = {selection:cursor_position}
editor.dispatch(mutation);
return true;
}
function cursorCharBackwardLogical(editor, range){
let cursor_position;
if(range.from == range.to){
cursor_position = EditorSelection.create([EditorSelection.range(
Math.max(range.from - 1, 0),
Math.max(range.from - 1, 0),
)]);
}
else{
cursor_position = EditorSelection.create([EditorSelection.range(
range.from,
range.from
)]);
}
const mutation = {selection:cursor_position}
editor.dispatch(mutation);
return true;
}
function cursorLogicalLeft(editor){
let range = editor.state.selection.ranges[0];
if(editor.textDirection == 1){ //RTL
return cursorCharForwardLogical(editor, range);
}
else{
return cursorCharBackwardLogical(editor, range);
}
}
function cursorLogicalRight(editor){
let range = editor.state.selection.ranges[0];
if(editor.textDirection == 1){ //RTL
return cursorCharBackwardLogical(editor, range);
}
else{
return cursorCharForwardLogical(editor, range);
}
}
function cursorGroupForwardLogical(editor, range){
// range.from == range.to
let doc = editor.state.doc.toString();
let i = range.from;
while(i < doc.length){
if(doc[i].match(/\p{Z}/u)){
i++;
}
else{
break
}
}
while(i < doc.length){
if(doc[i].match(/\p{L}|_|\p{N}|\p{M}/u)){
i++;
}
else{
break
}
}
if(i == range.from){
i = Math.min(i + 1, doc.length);
}
const cursor_position = EditorSelection.create([EditorSelection.range(
i,
i
)]);
const mutation = {selection:cursor_position}
editor.dispatch(mutation);
return true;
}
function cursorGroupBackwardLogical(editor, range){
// range.from == range.to
let doc = editor.state.doc.toString();
let i = range.from;
while(i > -1){
if(doc[i].match(/\p{Z}/u)){
i--;
}
else{
break
}
}
while(i > -1){
if(doc[i].match(/\p{L}|_|\p{N}|\p{M}/u)){
i--;
}
else{
break
}
}
if(i == range.from){
i = Math.max(i - 1, 0);
}
const cursor_position = EditorSelection.create([EditorSelection.range(
i,
i
)]);
const mutation = {selection:cursor_position}
editor.dispatch(mutation);
return true;
}
function cursorGroupLogicalLeft(editor){
let range = editor.state.selection.ranges[0];
if(range.from != range.to){
return cursorLogicalLeft(editor);
}
if(editor.textDirection == 1){ //RTL
return cursorGroupForwardLogical(editor, range);
}
else{
return cursorGroupBackwardLogical(editor, range);
}
}
function cursorGroupLogicalRight(editor){
let range = editor.state.selection.ranges[0];
if(range.from != range.to){
return cursorLogicalRight(editor);
}
if(editor.textDirection == 1){ //RTL
return cursorGroupBackwardLogical(editor, range);
}
else{
return cursorGroupForwardLogical(editor, range);
}
}
const LogicalKeyMap = [
{key: "ArrowLeft", run: cursorLogicalLeft, shift: selectCharLeft, preventDefault: true},
{key: "Mod-ArrowLeft", mac: "Alt-ArrowLeft", run: cursorGroupLogicalLeft, shift: selectGroupLeft, preventDefault: true},
{key: "ArrowRight", run: cursorLogicalRight, shift: selectCharRight, preventDefault: true},
{key: "Mod-ArrowRight", mac: "Alt-ArrowRight", run: cursorGroupLogicalRight, shift: selectGroupRight, preventDefault: true},
]
Now the cursor moves as expected: