virt2/api/soft/CodeMirror/view/test/test-selection.ts

91 lines
2.9 KiB
TypeScript
Executable File

import {tempEditor, requireFocus} from "./temp-editor"
import {EditorSelection} from "@codemirror/next/state"
import ist from "ist"
function setDOMSel(node: Node, offset: number) {
let range = document.createRange()
range.setEnd(node, offset)
range.setStart(node, offset)
let sel = window.getSelection()!
sel.removeAllRanges()
sel.addRange(range)
}
describe("EditorView selection", () => {
it("can read the DOM selection", () => {
let cm = requireFocus(tempEditor("one\n\nthree"))
function test(node: Node, offset: number, expected: number) {
setDOMSel(node, offset)
cm.contentDOM.focus()
cm.observer.flush()
ist(cm.state.selection.primary.head, expected)
}
let one = cm.contentDOM.firstChild!.firstChild!
let three = cm.contentDOM.lastChild!.firstChild!
test(one, 0, 0)
test(one, 1, 1)
test(one, 3, 3)
test(one.parentNode!, 0, 0)
test(one.parentNode!, 1, 3)
test(cm.contentDOM.childNodes[1], 0, 4)
test(three, 0, 5)
test(three, 2, 7)
test(three.parentNode!, 0, 5)
test(three.parentNode!, 1, 10)
})
it("syncs the DOM selection with the editor selection", () => {
let cm = requireFocus(tempEditor("abc\n\ndef"))
function test(pos: number, node: Node, offset: number) {
cm.dispatch(cm.state.t().setSelection(EditorSelection.single(pos)))
let sel = window.getSelection()!
ist(isEquivalentPosition(node, offset, sel.focusNode, sel.focusOffset))
}
let abc = cm.contentDOM.firstChild!.firstChild!
let def = cm.contentDOM.lastChild!.firstChild!
test(0, abc.parentNode!, 0)
test(1, abc, 1)
test(2, abc, 2)
test(3, abc.parentNode!, 1)
test(4, cm.contentDOM.childNodes[1], 0)
test(5, def.parentNode!, 0)
test(6, def, 1)
test(8, def.parentNode!, 1)
})
})
function isEquivalentPosition(node: Node, off: number, targetNode: Node | null, targetOff: number): boolean {
function scanFor(node: Node, off: number, targetNode: Node, targetOff: number, dir: -1 | 1): boolean {
for (;;) {
if (node == targetNode && off == targetOff) return true
if (off == (dir < 0 ? 0 : maxOffset(node))) {
if (node.nodeName == "DIV") return false
let parent = node.parentNode
if (!parent || parent.nodeType != 1) return false
off = domIndex(node) + (dir < 0 ? 0 : 1)
node = parent
} else if (node.nodeType == 1) {
node = node.childNodes[off + (dir < 0 ? -1 : 0)]
off = dir < 0 ? maxOffset(node) : 0
} else {
return false
}
}
}
function domIndex(node: Node): number {
for (var index = 0;; index++) {
node = node.previousSibling!
if (!node) return index
}
}
function maxOffset(node: Node): number {
return node.nodeType == 3 ? node.nodeValue!.length : node.childNodes.length
}
return targetNode ? (scanFor(node, off, targetNode, targetOff, -1) ||
scanFor(node, off, targetNode, targetOff, 1)) : false
}