From 959564ae90640529923c159822f33fbfc84a07d5 Mon Sep 17 00:00:00 2001
From: Adrian Heine <mail@adrianheine.de>
Date: Mon, 7 Nov 2016 14:18:17 +0100
Subject: [PATCH] Use stickiness in cursorCoords for bidi

---
 src/measurement/position_measurement.js | 50 +++++++++++++++----------
 src/util/bidi.js                        | 29 +++++---------
 2 files changed, 41 insertions(+), 38 deletions(-)

diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js
index 4e8a3f839..49581e656 100644
--- a/src/measurement/position_measurement.js
+++ b/src/measurement/position_measurement.js
@@ -2,7 +2,7 @@ import { buildLineContent, LineView } from "../line/line_data"
 import { clipPos, Pos } from "../line/pos"
 import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans"
 import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line"
-import { bidiLeft, bidiRight, bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi"
+import { bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi"
 import { ie, ie_version } from "../util/browser"
 import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom"
 import { e_target } from "../util/event"
@@ -334,6 +334,19 @@ export function charCoords(cm, pos, context, lineObj, bias) {
 // Returns a box for a given cursor position, which may have an
 // 'other' property containing the position of the secondary cursor
 // on a bidi boundary.
+// A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
+// and after `char - 1` in writing order of `char - 1`
+// A cursor Pos(line, char, "after") is on the same visual line as `char`
+// and before `char` in writing order of `char`
+// Examples (upper-case letters are RTL, lower-case are LTR):
+//     Pos(0, 1, ...)
+//     before   after
+// ab     a|b     a|b
+// aB     a|B     aB|
+// Ab     |Ab     A|b
+// AB     B|A     B|A
+// Every position after the last character on a line is considered to stick
+// to the last character on the line.
 export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
   lineObj = lineObj || getLine(cm.doc, pos.line)
   if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj)
@@ -342,25 +355,24 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig
     if (right) m.left = m.right; else m.right = m.left
     return intoCoordSystem(cm, lineObj, m, context)
   }
-  function getBidi(ch, partPos) {
-    let part = order[partPos], right = part.level % 2
-    if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
-      part = order[--partPos]
-      ch = bidiRight(part) - (part.level % 2 ? 0 : 1)
-      right = true
-    } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
-      part = order[++partPos]
-      ch = bidiLeft(part) - part.level % 2
-      right = false
-    }
-    if (right && ch == part.to && ch > part.from) return get(ch - 1)
-    return get(ch, right)
+  let order = getOrder(lineObj), ch = pos.ch, sticky = pos.sticky
+  if (ch >= lineObj.text.length) {
+    ch = lineObj.text.length
+    sticky = "before"
+  } else if (ch <= 0) {
+    ch = 0
+    sticky = "after"
+  }
+  if (!order) return get(sticky == "before" ? ch - 1 : ch, sticky == "before")
+
+  function getBidi(ch, partPos, invert) {
+    let part = order[partPos], right = (part.level % 2) != 0
+    return get(invert ? ch - 1 : ch, right != invert)
   }
-  let order = getOrder(lineObj), ch = pos.ch
-  if (!order) return get(pos.sticky == "before" ? ch - 1 : ch, pos.sticky == "before")
-  let partPos = getBidiPartAt(order, ch)
-  let val = getBidi(ch, partPos)
-  if (bidiOther != null) val.other = getBidi(ch, bidiOther)
+  let partPos = getBidiPartAt(order, ch, sticky)
+  let other = bidiOther
+  let val = getBidi(ch, partPos, sticky == "before")
+  if (other != null) val.other = getBidi(ch, other, sticky != "before")
   return val
 }
 
diff --git a/src/util/bidi.js b/src/util/bidi.js
index 6a8491403..7d08e3659 100644
--- a/src/util/bidi.js
+++ b/src/util/bidi.js
@@ -25,33 +25,24 @@ export function lineRight(line) {
   return bidiRight(lst(order))
 }
 
-function compareBidiLevel(order, a, b) {
-  let linedir = order[0].level
-  if (a == linedir) return true
-  if (b == linedir) return false
-  return a < b
-}
 
 export let bidiOther = null
-export function getBidiPartAt(order, pos) {
+export function getBidiPartAt(order, ch, sticky) {
   let found
   bidiOther = null
   for (let i = 0; i < order.length; ++i) {
     let cur = order[i]
-    if (cur.from < pos && cur.to > pos) return i
-    if ((cur.from == pos || cur.to == pos)) {
-      if (found == null) {
-        found = i
-      } else if (compareBidiLevel(order, cur.level, order[found].level)) {
-        if (cur.from != cur.to) bidiOther = found
-        return i
-      } else {
-        if (cur.from != cur.to) bidiOther = i
-        return found
-      }
+    if (cur.from < ch && cur.to > ch) return i
+    if (cur.to == ch) {
+      if (cur.from != cur.to && sticky == "before") found = i
+      else bidiOther = i
+    }
+    if (cur.from == ch) {
+      if (cur.from != cur.to && sticky != "before") found = i
+      else bidiOther = i
     }
   }
-  return found
+  return found != null ? found : bidiOther
 }
 
 function moveInLine(line, pos, dir, byUnit) {
-- 
GitLab