(cm, line, start, dir)
| 6868 | } |
| 6869 | |
| 6870 | function moveVisually(cm, line, start, dir) { |
| 6871 | var bidi = getOrder(line, cm.doc.direction); |
| 6872 | if (!bidi) { return moveLogically(line, start, dir) } |
| 6873 | if (start.ch >= line.text.length) { |
| 6874 | start.ch = line.text.length; |
| 6875 | start.sticky = "before"; |
| 6876 | } else if (start.ch <= 0) { |
| 6877 | start.ch = 0; |
| 6878 | start.sticky = "after"; |
| 6879 | } |
| 6880 | var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; |
| 6881 | if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { |
| 6882 | // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, |
| 6883 | // nothing interesting happens. |
| 6884 | return moveLogically(line, start, dir) |
| 6885 | } |
| 6886 | |
| 6887 | var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; |
| 6888 | var prep; |
| 6889 | var getWrappedLineExtent = function (ch) { |
| 6890 | if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } |
| 6891 | prep = prep || prepareMeasureForLine(cm, line); |
| 6892 | return wrappedLineExtentChar(cm, line, prep, ch) |
| 6893 | }; |
| 6894 | var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); |
| 6895 | |
| 6896 | if (cm.doc.direction == "rtl" || part.level == 1) { |
| 6897 | var moveInStorageOrder = (part.level == 1) == (dir < 0); |
| 6898 | var ch = mv(start, moveInStorageOrder ? 1 : -1); |
| 6899 | if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { |
| 6900 | // Case 2: We move within an rtl part or in an rtl editor on the same visual line |
| 6901 | var sticky = moveInStorageOrder ? "before" : "after"; |
| 6902 | return new Pos(start.line, ch, sticky) |
| 6903 | } |
| 6904 | } |
| 6905 | |
| 6906 | // Case 3: Could not move within this bidi part in this visual line, so leave |
| 6907 | // the current bidi part |
| 6908 | |
| 6909 | var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { |
| 6910 | var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder |
| 6911 | ? new Pos(start.line, mv(ch, 1), "before") |
| 6912 | : new Pos(start.line, ch, "after"); }; |
| 6913 | |
| 6914 | for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { |
| 6915 | var part = bidi[partPos]; |
| 6916 | var moveInStorageOrder = (dir > 0) == (part.level != 1); |
| 6917 | var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); |
| 6918 | if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } |
| 6919 | ch = moveInStorageOrder ? part.from : mv(part.to, -1); |
| 6920 | if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } |
| 6921 | } |
| 6922 | }; |
| 6923 | |
| 6924 | // Case 3a: Look for other bidi parts on the same visual line |
| 6925 | var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); |
| 6926 | if (res) { return res } |
| 6927 |
no test coverage detected