annotate .cms/lib/codemirror/src/input/movement.js @ 0:78edf6b517a0 draft

24.10
author Coffee CMS <info@coffee-cms.ru>
date Fri, 11 Oct 2024 22:40:23 +0000
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
1 import { Pos } from "../line/pos.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
2 import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
3 import { getBidiPartAt, getOrder } from "../util/bidi.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
4 import { findFirst, lst, skipExtendingChars } from "../util/misc.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
5
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
6 function moveCharLogically(line, ch, dir) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
7 let target = skipExtendingChars(line.text, ch + dir, dir)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
8 return target < 0 || target > line.text.length ? null : target
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
9 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
10
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
11 export function moveLogically(line, start, dir) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
12 let ch = moveCharLogically(line, start.ch, dir)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
13 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
14 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
15
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
16 export function endOfLine(visually, cm, lineObj, lineNo, dir) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
17 if (visually) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
18 if (cm.doc.direction == "rtl") dir = -dir
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
19 let order = getOrder(lineObj, cm.doc.direction)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
20 if (order) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
21 let part = dir < 0 ? lst(order) : order[0]
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
22 let moveInStorageOrder = (dir < 0) == (part.level == 1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
23 let sticky = moveInStorageOrder ? "after" : "before"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
24 let ch
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
25 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
26 // it could be that the last bidi part is not on the last visual line,
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
27 // since visual lines contain content order-consecutive chunks.
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
28 // Thus, in rtl, we are looking for the first (content-order) character
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
29 // in the rtl chunk that is on the last line (that is, the same line
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
30 // as the last (content-order) character).
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
31 if (part.level > 0 || cm.doc.direction == "rtl") {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
32 let prep = prepareMeasureForLine(cm, lineObj)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
33 ch = dir < 0 ? lineObj.text.length - 1 : 0
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
34 let targetTop = measureCharPrepared(cm, prep, ch).top
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
35 ch = findFirst(ch => measureCharPrepared(cm, prep, ch).top == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
36 if (sticky == "before") ch = moveCharLogically(lineObj, ch, 1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
37 } else ch = dir < 0 ? part.to : part.from
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
38 return new Pos(lineNo, ch, sticky)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
39 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
40 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
41 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
42 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
43
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
44 export function moveVisually(cm, line, start, dir) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
45 let bidi = getOrder(line, cm.doc.direction)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
46 if (!bidi) return moveLogically(line, start, dir)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
47 if (start.ch >= line.text.length) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
48 start.ch = line.text.length
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
49 start.sticky = "before"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
50 } else if (start.ch <= 0) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
51 start.ch = 0
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
52 start.sticky = "after"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
53 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
54 let partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
55 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
56 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
57 // nothing interesting happens.
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
58 return moveLogically(line, start, dir)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
59 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
60
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
61 let mv = (pos, dir) => moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
62 let prep
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
63 let getWrappedLineExtent = ch => {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
64 if (!cm.options.lineWrapping) return {begin: 0, end: line.text.length}
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
65 prep = prep || prepareMeasureForLine(cm, line)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
66 return wrappedLineExtentChar(cm, line, prep, ch)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
67 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
68 let wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
69
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
70 if (cm.doc.direction == "rtl" || part.level == 1) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
71 let moveInStorageOrder = (part.level == 1) == (dir < 0)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
72 let ch = mv(start, moveInStorageOrder ? 1 : -1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
73 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
74 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
75 let sticky = moveInStorageOrder ? "before" : "after"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
76 return new Pos(start.line, ch, sticky)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
77 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
78 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
79
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
80 // Case 3: Could not move within this bidi part in this visual line, so leave
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
81 // the current bidi part
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
82
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
83 let searchInVisualLine = (partPos, dir, wrappedLineExtent) => {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
84 let getRes = (ch, moveInStorageOrder) => moveInStorageOrder
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
85 ? new Pos(start.line, mv(ch, 1), "before")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
86 : new Pos(start.line, ch, "after")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
87
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
88 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
89 let part = bidi[partPos]
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
90 let moveInStorageOrder = (dir > 0) == (part.level != 1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
91 let ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
92 if (part.from <= ch && ch < part.to) return getRes(ch, moveInStorageOrder)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
93 ch = moveInStorageOrder ? part.from : mv(part.to, -1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
94 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) return getRes(ch, moveInStorageOrder)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
95 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
96 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
97
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
98 // Case 3a: Look for other bidi parts on the same visual line
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
99 let res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
100 if (res) return res
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
101
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
102 // Case 3b: Look for other bidi parts on the next visual line
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
103 let nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
104 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
105 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
106 if (res) return res
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
107 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
108
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
109 // Case 4: Nowhere to move
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
110 return null
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
111 }