Search code examples
javascriptcoffeescriptatom-editorright-to-left

Fixing Atom cursor's behaviour when switching to right to left languages


I have been trying to change the default settings of Atom, the text editor, to support RTL (right-to-left)languages.

So on class LinesTileComponent, I added a new attribute dir="rtl" here.

This has switched the entire script to switch to right as shown here.

enter image description here

The cursor disappears when typing Arabic. Any clicks on the texts do NOT bring back the cursor (happens later in the GIF). There is no way I could select a specific word out of the line and the cursor only appears at the left when I click after the RTL text.

I'm suspecting this could be due to the code on cursor.js, cursor.less or in selection.js or others.

I'm struggling with fixing this cursor's behaviour. Are there any specific files or quick fixes that you could help with to fix this issue?


Solution

  • This has been a tough bug to fix. The fix has been submitted for review on Github PR for RTL to check out and contribute to.

    enter image description here

    In summary, the developers used a binary search algorithm to find which letter the user clicked on. The binary search approach assumed that the second half of the word is always on the right. This is the opposite in RTL language since the second half of the work is on the left. Here's a snippet on how I prototyped the fix:

      let characterIndex = 0;
        {
          let low = 0;
          let high = containingTextNode.length - 1;
          while (low <= high) {
            const charIndex = low + ((high - low) >> 1);
            const nextCharIndex = isPairedCharacter(
              containingTextNode.textContent,
              charIndex
            )
              ? charIndex + 2
              : charIndex + 1;
    
            const rangeRect = clientRectForRange(
              containingTextNode,
              charIndex,
              nextCharIndex
            );
            if (targetClientLeft < rangeRect.left && !rtl) {
              high = charIndex - 1;
              characterIndex = Math.max(0, charIndex - 1);
            } else if (targetClientLeft > rangeRect.right && !rtl) {
              low = nextCharIndex;
              characterIndex = Math.min(
                containingTextNode.textContent.length,
                nextCharIndex
              );
            } else if (targetClientLeft > rangeRect.right && rtl) {
              high = charIndex - 1;
              characterIndex = Math.max(0, charIndex - 1);
            } else if (targetClientLeft < rangeRect.left && rtl) {
              low = nextCharIndex;
              characterIndex = Math.min(
                containingTextNode.textContent.length,
                nextCharIndex
              );
            } else {
              if (!rtl){
                if (targetClientLeft <= (rangeRect.left + rangeRect.right) / 2) {
                  characterIndex = charIndex;
                } else {
                  characterIndex = nextCharIndex;
                }
              }else{
                if (targetClientLeft <= (rangeRect.left + rangeRect.right) / 2) {
                  characterIndex = nextCharIndex;
                } else {
                  characterIndex = charIndex;
                }
              }
              break;
            }
          }
        }
    

    Unfortunately this code is still missing few features like handling RTL and LTR content in the same line and other minor bugs. More work should be done and collaborations are open, please contribute to this PR.