0
|
1 import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js"
|
|
2 import { indexOf } from "../util/misc.js"
|
|
3
|
|
4 // Selection objects are immutable. A new one is created every time
|
|
5 // the selection changes. A selection is one or more non-overlapping
|
|
6 // (and non-touching) ranges, sorted, and an integer that indicates
|
|
7 // which one is the primary selection (the one that's scrolled into
|
|
8 // view, that getCursor returns, etc).
|
|
9 export class Selection {
|
|
10 constructor(ranges, primIndex) {
|
|
11 this.ranges = ranges
|
|
12 this.primIndex = primIndex
|
|
13 }
|
|
14
|
|
15 primary() { return this.ranges[this.primIndex] }
|
|
16
|
|
17 equals(other) {
|
|
18 if (other == this) return true
|
|
19 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false
|
|
20 for (let i = 0; i < this.ranges.length; i++) {
|
|
21 let here = this.ranges[i], there = other.ranges[i]
|
|
22 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false
|
|
23 }
|
|
24 return true
|
|
25 }
|
|
26
|
|
27 deepCopy() {
|
|
28 let out = []
|
|
29 for (let i = 0; i < this.ranges.length; i++)
|
|
30 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head))
|
|
31 return new Selection(out, this.primIndex)
|
|
32 }
|
|
33
|
|
34 somethingSelected() {
|
|
35 for (let i = 0; i < this.ranges.length; i++)
|
|
36 if (!this.ranges[i].empty()) return true
|
|
37 return false
|
|
38 }
|
|
39
|
|
40 contains(pos, end) {
|
|
41 if (!end) end = pos
|
|
42 for (let i = 0; i < this.ranges.length; i++) {
|
|
43 let range = this.ranges[i]
|
|
44 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
|
|
45 return i
|
|
46 }
|
|
47 return -1
|
|
48 }
|
|
49 }
|
|
50
|
|
51 export class Range {
|
|
52 constructor(anchor, head) {
|
|
53 this.anchor = anchor; this.head = head
|
|
54 }
|
|
55
|
|
56 from() { return minPos(this.anchor, this.head) }
|
|
57 to() { return maxPos(this.anchor, this.head) }
|
|
58 empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }
|
|
59 }
|
|
60
|
|
61 // Take an unsorted, potentially overlapping set of ranges, and
|
|
62 // build a selection out of it. 'Consumes' ranges array (modifying
|
|
63 // it).
|
|
64 export function normalizeSelection(cm, ranges, primIndex) {
|
|
65 let mayTouch = cm && cm.options.selectionsMayTouch
|
|
66 let prim = ranges[primIndex]
|
|
67 ranges.sort((a, b) => cmp(a.from(), b.from()))
|
|
68 primIndex = indexOf(ranges, prim)
|
|
69 for (let i = 1; i < ranges.length; i++) {
|
|
70 let cur = ranges[i], prev = ranges[i - 1]
|
|
71 let diff = cmp(prev.to(), cur.from())
|
|
72 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
|
|
73 let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
|
|
74 let inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
|
|
75 if (i <= primIndex) --primIndex
|
|
76 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
|
|
77 }
|
|
78 }
|
|
79 return new Selection(ranges, primIndex)
|
|
80 }
|
|
81
|
|
82 export function simpleSelection(anchor, head) {
|
|
83 return new Selection([new Range(anchor, head || anchor)], 0)
|
|
84 }
|