3d118251bef971e1434eceaeb78dfc5115f2844d4fd8aaf2fc2161bc0473b3b96d4d04c35271675781f04d762607ea49e04debf81307392c27d9284287f289 2.8 KB

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