annotate .cms/lib/codemirror/src/display/selection.js @ 1:1d486627aa1e draft default tip

24.10
author Coffee CMS <info@coffee-cms.ru>
date Sat, 12 Oct 2024 02:51:39 +0000
parents 78edf6b517a0
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 { visualLine } from "../line/spans.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
3 import { getLine } from "../line/utils_line.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
4 import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
5 import { getOrder, iterateBidiSections } from "../util/bidi.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
6 import { elt } from "../util/dom.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
7 import { onBlur } from "./focus.js"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
8
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
9 export function updateSelection(cm) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
10 cm.display.input.showSelection(cm.display.input.prepareSelection())
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
11 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
12
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
13 export function prepareSelection(cm, primary = true) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
14 let doc = cm.doc, result = {}
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
15 let curFragment = result.cursors = document.createDocumentFragment()
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
16 let selFragment = result.selection = document.createDocumentFragment()
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
17
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
18 let customCursor = cm.options.$customCursor
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
19 if (customCursor) primary = true
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
20 for (let i = 0; i < doc.sel.ranges.length; i++) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
21 if (!primary && i == doc.sel.primIndex) continue
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
22 let range = doc.sel.ranges[i]
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
23 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
24 let collapsed = range.empty()
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
25 if (customCursor) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
26 let head = customCursor(cm, range)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
27 if (head) drawSelectionCursor(cm, head, curFragment)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
28 } else if (collapsed || cm.options.showCursorWhenSelecting) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
29 drawSelectionCursor(cm, range.head, curFragment)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
30 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
31 if (!collapsed)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
32 drawSelectionRange(cm, range, selFragment)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
33 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
34 return result
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
35 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
36
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
37 // Draws a cursor for the given range
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
38 export function drawSelectionCursor(cm, head, output) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
39 let pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
40
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
41 let cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
42 cursor.style.left = pos.left + "px"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
43 cursor.style.top = pos.top + "px"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
44 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
45
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
46 if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
47 let charPos = charCoords(cm, head, "div", null, null)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
48 let width = charPos.right - charPos.left
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
49 cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
50 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
51
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
52 if (pos.other) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
53 // Secondary cursor, shown when on a 'jump' in bi-directional text
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
54 let otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
55 otherCursor.style.display = ""
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
56 otherCursor.style.left = pos.other.left + "px"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
57 otherCursor.style.top = pos.other.top + "px"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
58 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
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
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
62 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
63
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
64 // Draws the given range as a highlighted selection
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
65 function drawSelectionRange(cm, range, output) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
66 let display = cm.display, doc = cm.doc
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
67 let fragment = document.createDocumentFragment()
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
68 let padding = paddingH(cm.display), leftSide = padding.left
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
69 let rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
70 let docLTR = doc.direction == "ltr"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
71
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
72 function add(left, top, width, bottom) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
73 if (top < 0) top = 0
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
74 top = Math.round(top)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
75 bottom = Math.round(bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
76 fragment.appendChild(elt("div", null, "CodeMirror-selected", `position: absolute; left: ${left}px;
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
77 top: ${top}px; width: ${width == null ? rightSide - left : width}px;
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
78 height: ${bottom - top}px`))
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
79 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
80
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
81 function drawForLine(line, fromArg, toArg) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
82 let lineObj = getLine(doc, line)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
83 let lineLen = lineObj.text.length
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
84 let start, end
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
85 function coords(ch, bias) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
86 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
87 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
88
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
89 function wrapX(pos, dir, side) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
90 let extent = wrappedLineExtentChar(cm, lineObj, null, pos)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
91 let prop = (dir == "ltr") == (side == "after") ? "left" : "right"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
92 let ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
93 return coords(ch, prop)[prop]
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
94 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
95
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
96 let order = getOrder(lineObj, doc.direction)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
97 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
98 let ltr = dir == "ltr"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
99 let fromPos = coords(from, ltr ? "left" : "right")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
100 let toPos = coords(to - 1, ltr ? "right" : "left")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
101
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
102 let openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
103 let first = i == 0, last = !order || i == order.length - 1
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
104 if (toPos.top - fromPos.top <= 3) { // Single line
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
105 let openLeft = (docLTR ? openStart : openEnd) && first
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
106 let openRight = (docLTR ? openEnd : openStart) && last
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
107 let left = openLeft ? leftSide : (ltr ? fromPos : toPos).left
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
108 let right = openRight ? rightSide : (ltr ? toPos : fromPos).right
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
109 add(left, fromPos.top, right - left, fromPos.bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
110 } else { // Multiple lines
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
111 let topLeft, topRight, botLeft, botRight
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
112 if (ltr) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
113 topLeft = docLTR && openStart && first ? leftSide : fromPos.left
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
114 topRight = docLTR ? rightSide : wrapX(from, dir, "before")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
115 botLeft = docLTR ? leftSide : wrapX(to, dir, "after")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
116 botRight = docLTR && openEnd && last ? rightSide : toPos.right
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
117 } else {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
118 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
119 topRight = !docLTR && openStart && first ? rightSide : fromPos.right
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
120 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
121 botRight = !docLTR ? rightSide : wrapX(to, dir, "after")
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
122 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
123 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
124 if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
125 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
126 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
127
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
128 if (!start || cmpCoords(fromPos, start) < 0) start = fromPos
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
129 if (cmpCoords(toPos, start) < 0) start = toPos
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
130 if (!end || cmpCoords(fromPos, end) < 0) end = fromPos
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
131 if (cmpCoords(toPos, end) < 0) end = toPos
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
132 })
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
133 return {start: start, end: end}
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
134 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
135
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
136 let sFrom = range.from(), sTo = range.to()
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
137 if (sFrom.line == sTo.line) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
138 drawForLine(sFrom.line, sFrom.ch, sTo.ch)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
139 } else {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
140 let fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
141 let singleVLine = visualLine(fromLine) == visualLine(toLine)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
142 let leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
143 let rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
144 if (singleVLine) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
145 if (leftEnd.top < rightStart.top - 2) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
146 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
147 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
148 } else {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
149 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
150 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
151 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
152 if (leftEnd.bottom < rightStart.top)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
153 add(leftSide, leftEnd.bottom, null, rightStart.top)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
154 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
155
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
156 output.appendChild(fragment)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
157 }
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
158
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
159 // Cursor-blinking
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
160 export function restartBlink(cm) {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
161 if (!cm.state.focused) return
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
162 let display = cm.display
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
163 clearInterval(display.blinker)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
164 let on = true
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
165 display.cursorDiv.style.visibility = ""
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
166 if (cm.options.cursorBlinkRate > 0)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
167 display.blinker = setInterval(() => {
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
168 if (!cm.hasFocus()) onBlur(cm)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
169 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
170 }, cm.options.cursorBlinkRate)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
171 else if (cm.options.cursorBlinkRate < 0)
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
172 display.cursorDiv.style.visibility = "hidden"
Coffee CMS <info@coffee-cms.ru>
parents:
diff changeset
173 }