datafolder 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +4 -1
  2. package/index.js +1 -1
  3. package/internal.md +5 -3
  4. package/package.json +7 -5
  5. package/scripts/airbnb.js +77 -72
  6. package/scripts/airbnb2.js +78 -73
  7. package/scripts/airbnb2_queries.js +181 -229
  8. package/scripts/airbnb_queries.js +214 -265
  9. package/scripts/convertBinFiles.js +47 -49
  10. package/scripts/downloadFile.js +41 -40
  11. package/scripts/express.js +44 -48
  12. package/scripts/fastify.js +71 -80
  13. package/scripts/flights.js +71 -75
  14. package/scripts/flights2.js +78 -81
  15. package/scripts/flights2_queries.js +417 -455
  16. package/scripts/flights_queries.js +381 -407
  17. package/src/QyAcl.d.ts +17 -0
  18. package/src/QyAcl.js +113 -116
  19. package/src/QyAclSaver.d.ts +4 -0
  20. package/src/QyAclSaver.js +3 -3
  21. package/src/QyAclSaver_Worker.d.ts +9 -0
  22. package/src/QyAclSaver_Worker.js +2 -4
  23. package/src/QyAclWriter.d.ts +5 -0
  24. package/src/QyBatch.d.ts +42 -0
  25. package/src/QyBatch.js +8 -8
  26. package/src/QyBinWriter.d.ts +15 -0
  27. package/src/QyCD.d.ts +37 -0
  28. package/src/QyCD.js +8 -6
  29. package/src/QyCache.d.ts +44 -0
  30. package/src/QyCache.js +65 -59
  31. package/src/QyCompressor.d.ts +9 -0
  32. package/src/QyCompressor.js +29 -29
  33. package/src/QyCompressor_Worker.d.ts +1 -0
  34. package/src/QyDB.d.ts +42 -0
  35. package/src/QyDB.js +13 -11
  36. package/src/QyDefaultOptions.d.ts +1 -0
  37. package/src/QyDefaultOptions.js +3 -0
  38. package/src/QyDir.d.ts +48 -0
  39. package/src/QyDir.js +41 -41
  40. package/src/QyFetch.d.ts +7 -0
  41. package/src/QyFetch.js +2 -3
  42. package/src/QyFile.d.ts +19 -0
  43. package/src/QyFile.js +35 -34
  44. package/src/QyFileContentUtils.d.ts +13 -0
  45. package/src/QyFileContentUtils.js +126 -126
  46. package/src/QyFileLogger.d.ts +11 -0
  47. package/src/QyIndex.d.ts +6 -0
  48. package/src/QyIndex.js +73 -75
  49. package/src/QyIndexPropTree.d.ts +7 -0
  50. package/src/QyIndexPropTree.js +24 -24
  51. package/src/QyIntersectNode.d.ts +7 -0
  52. package/src/QyIntersectNode.js +15 -15
  53. package/src/QyItem.d.ts +30 -0
  54. package/src/QyItem.js +32 -28
  55. package/src/QyJsonSaver.d.ts +6 -0
  56. package/src/QyKVData.d.ts +12 -0
  57. package/src/QyKVData.js +11 -10
  58. package/src/QyKVDataCleaner.d.ts +4 -0
  59. package/src/QyKVDataCleaner.js +1 -1
  60. package/src/QyKVDataCleaner_Worker.d.ts +13 -0
  61. package/src/QyKVDataCleaner_Worker.js +2 -4
  62. package/src/QyKVDataLoader.d.ts +9 -0
  63. package/src/QyKVDataLoader.js +11 -11
  64. package/src/QyKVDataSaver.d.ts +4 -0
  65. package/src/QyKVDataSaver.js +6 -6
  66. package/src/QyKVDataSaver_Worker.d.ts +13 -0
  67. package/src/QyKVDataSaver_Worker.js +7 -9
  68. package/src/QyLogFileSaver.d.ts +4 -0
  69. package/src/QyLogFileSaver.js +1 -1
  70. package/src/QyLogFileSaver_Worker.d.ts +8 -0
  71. package/src/QyLogFileSaver_Worker.js +2 -4
  72. package/src/QyLogger.d.ts +10 -0
  73. package/src/QyLogger.js +40 -40
  74. package/src/QyMessageWorker.d.ts +5 -0
  75. package/src/QyMessageWorker.js +7 -7
  76. package/src/QyMessager.d.ts +7 -0
  77. package/src/QyMessager.js +30 -27
  78. package/src/QyMessager_Run.d.ts +1 -0
  79. package/src/QyMessager_Run.js +11 -6
  80. package/src/QyMod.d.ts +1 -0
  81. package/src/QyMod.js +6 -6
  82. package/src/QyOpRunner.d.ts +10 -0
  83. package/src/QyOpRunner.js +39 -39
  84. package/src/QyQueue.d.ts +11 -0
  85. package/src/QySaver.d.ts +10 -0
  86. package/src/QySnapshotCompactor.d.ts +4 -0
  87. package/src/QySnapshotCompactor.js +1 -1
  88. package/src/QySnapshotCompactor_Worker.d.ts +11 -0
  89. package/src/QySnapshotCompactor_Worker.js +2 -4
  90. package/src/QySnapshotSaver.d.ts +4 -0
  91. package/src/QySnapshotSaver.js +1 -1
  92. package/src/QySnapshotSaver_Worker.d.ts +16 -0
  93. package/src/QySnapshotSaver_Worker.js +17 -16
  94. package/src/QySnapshots.d.ts +27 -0
  95. package/src/QySnapshots.js +3 -3
  96. package/src/QyStructure.d.ts +1 -0
  97. package/src/QyStructure.js +18 -18
  98. package/src/QyTrigger.d.ts +9 -0
  99. package/src/QyTrigger.js +30 -30
  100. package/src/QyUnionNode.d.ts +17 -0
  101. package/src/QyUnionNode.js +2 -2
  102. package/src/QyUtils.d.ts +53 -0
  103. package/src/QyUtils.js +140 -117
  104. package/src/QyWorker.d.ts +5 -0
  105. package/src/QyWorker.js +18 -18
  106. package/src/hackernews.d.ts +1 -0
  107. package/src/hackernews.js +59 -47
  108. package/src/rpc/getRpcFiles.js +1 -0
  109. package/src/simulate.d.ts +1 -0
  110. package/src/simulate2.d.ts +1 -0
  111. package/src/soc_pokec.d.ts +1 -0
  112. package/static/codemirror.bundle/editor.bundle.js +399 -184
  113. package/static/datafolderui/QyStyledElement.js +2 -2
  114. package/static/datafolderui/QyUIFolderViewer.js +97 -77
  115. package/static/datafolderui/QyUIUtils.js +4 -4
  116. package/static/datafolderui/code-mirror.js +7 -7
  117. package/static/datafolderui/display-tree.js +13 -10
  118. package/static/datafolderui/hscrpt.js +26 -7
  119. package/static/datafolderui/json-view.js +27 -0
  120. package/static/datafolderui/main-page.js +65 -37
  121. package/static/datafolderui/query-editor.js +8 -8
  122. package/static/datafolderui/rpc-editor.js +31 -0
  123. package/static/datafolderui/trigger-editor.js +9 -6
  124. package/static/datafolderui/QyUIFolderTree.js +0 -188
@@ -1406,11 +1406,11 @@
1406
1406
  /**
1407
1407
  Extend this range to cover at least `from` to `to`.
1408
1408
  */
1409
- extend(from, to = from) {
1409
+ extend(from, to = from, assoc = 0) {
1410
1410
  if (from <= this.anchor && to >= this.anchor)
1411
- return EditorSelection.range(from, to);
1411
+ return EditorSelection.range(from, to, undefined, undefined, assoc);
1412
1412
  let head = Math.abs(from - this.anchor) > Math.abs(to - this.anchor) ? from : to;
1413
- return EditorSelection.range(this.anchor, head);
1413
+ return EditorSelection.range(this.anchor, head, undefined, undefined, assoc);
1414
1414
  }
1415
1415
  /**
1416
1416
  Compare this range to another range.
@@ -1557,11 +1557,13 @@
1557
1557
  /**
1558
1558
  Create a selection range.
1559
1559
  */
1560
- static range(anchor, head, goalColumn, bidiLevel) {
1560
+ static range(anchor, head, goalColumn, bidiLevel, assoc) {
1561
1561
  let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) |
1562
1562
  (bidiLevel == null ? 7 : Math.min(6, bidiLevel));
1563
+ if (!assoc && anchor != head)
1564
+ assoc = head < anchor ? 1 : -1;
1563
1565
  return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags)
1564
- : SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags);
1566
+ : SelectionRange.create(anchor, head, (!assoc ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) | flags);
1565
1567
  }
1566
1568
  /**
1567
1569
  @internal
@@ -4418,7 +4420,7 @@
4418
4420
  couldn't (in which case the widget will be redrawn). The default
4419
4421
  implementation just returns false.
4420
4422
  */
4421
- updateDOM(dom, view) { return false; }
4423
+ updateDOM(dom, view, from) { return false; }
4422
4424
  /**
4423
4425
  @internal
4424
4426
  */
@@ -4907,16 +4909,16 @@
4907
4909
  }
4908
4910
  }
4909
4911
  }
4910
- function scrollableParents(dom) {
4911
- let doc = dom.ownerDocument, x, y;
4912
+ function scrollableParents(dom, getX = true) {
4913
+ let doc = dom.ownerDocument, x = null, y = null;
4912
4914
  for (let cur = dom.parentNode; cur;) {
4913
- if (cur == doc.body || (x && y)) {
4915
+ if (cur == doc.body || ((!getX || x) && y)) {
4914
4916
  break;
4915
4917
  }
4916
4918
  else if (cur.nodeType == 1) {
4917
4919
  if (!y && cur.scrollHeight > cur.clientHeight)
4918
4920
  y = cur;
4919
- if (!x && cur.scrollWidth > cur.clientWidth)
4921
+ if (getX && !x && cur.scrollWidth > cur.clientWidth)
4920
4922
  x = cur;
4921
4923
  cur = cur.assignedSlot || cur.parentNode;
4922
4924
  }
@@ -5041,6 +5043,8 @@
5041
5043
  }
5042
5044
  }
5043
5045
  function isScrolledToBottom(elt) {
5046
+ if (elt instanceof Window)
5047
+ return elt.pageYOffset > Math.max(0, elt.document.documentElement.scrollHeight - elt.innerHeight - 4);
5044
5048
  return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
5045
5049
  }
5046
5050
  function textNodeBefore(startNode, startOffset) {
@@ -6819,7 +6823,7 @@
6819
6823
  let tile = widgets[i];
6820
6824
  if (!this.reused.has(tile) &&
6821
6825
  (pass == 0 ? tile.widget.compare(widget)
6822
- : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view))) {
6826
+ : tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view, tile.widget))) {
6823
6827
  widgets.splice(i, 1);
6824
6828
  if (i < this.index[0])
6825
6829
  this.index[0]--;
@@ -6938,7 +6942,7 @@
6938
6942
  }
6939
6943
  else if (tile.isText()) {
6940
6944
  this.builder.ensureLine(null);
6941
- if (!from && to == tile.length) {
6945
+ if (!from && to == tile.length && !this.cache.reused.has(tile)) {
6942
6946
  this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
6943
6947
  }
6944
6948
  else {
@@ -7073,9 +7077,10 @@
7073
7077
  marks.push(tile);
7074
7078
  else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
7075
7079
  line = tile;
7080
+ else if (tile instanceof BlockWrapperTile) ; // Ignore
7076
7081
  else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
7077
7082
  line = new LineTile(parent, lineBaseAttrs);
7078
- else
7083
+ else if (!line)
7079
7084
  marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
7080
7085
  }
7081
7086
  return { line: line, marks };
@@ -7241,6 +7246,8 @@
7241
7246
  if (composition || changes.length) {
7242
7247
  let oldTile = this.tile;
7243
7248
  let builder = new TileUpdate(this.view, oldTile, this.blockWrappers, this.decorations, this.dynamicDecorationMap);
7249
+ if (composition && Tile.get(composition.text))
7250
+ builder.cache.reused.set(Tile.get(composition.text), 2 /* Reused.DOM */);
7244
7251
  this.tile = builder.run(changes, composition);
7245
7252
  destroyDropped(oldTile, builder.cache.reused);
7246
7253
  }
@@ -7672,6 +7679,7 @@
7672
7679
  this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
7673
7680
  }
7674
7681
  scrollIntoView(target) {
7682
+ var _a;
7675
7683
  if (target.isSnapshot) {
7676
7684
  let ref = this.view.viewState.lineBlockAt(target.range.head);
7677
7685
  this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
@@ -7688,7 +7696,7 @@
7688
7696
  }
7689
7697
  }
7690
7698
  let { range } = target;
7691
- let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
7699
+ let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
7692
7700
  if (!rect)
7693
7701
  return;
7694
7702
  if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
@@ -7701,6 +7709,19 @@
7701
7709
  };
7702
7710
  let { offsetWidth, offsetHeight } = this.view.scrollDOM;
7703
7711
  scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == Direction.LTR);
7712
+ // On mobile browsers, the visual viewport may be smaller than the
7713
+ // actual reported viewport, causing scrollRectIntoView to fail to
7714
+ // scroll properly. Unfortunately, this visual viewport cannot be
7715
+ // updated directly, and scrollIntoView is the only way a script
7716
+ // can affect it. So this tries to kludge around the problem by
7717
+ // calling scrollIntoView on the scroll target's line.
7718
+ if (window.visualViewport && window.innerHeight - window.visualViewport.height > 1 &&
7719
+ (rect.top > window.pageYOffset + window.visualViewport.offsetTop + window.visualViewport.height ||
7720
+ rect.bottom < window.pageYOffset + window.visualViewport.offsetTop)) {
7721
+ let line = this.view.docView.lineAt(range.head, 1);
7722
+ if (line)
7723
+ line.dom.scrollIntoView({ block: "nearest" });
7724
+ }
7704
7725
  }
7705
7726
  lineHasWidget(pos) {
7706
7727
  let scan = (child) => child.isWidget() || child.children.some(scan);
@@ -7942,7 +7963,8 @@
7942
7963
  return EditorSelection.cursor(startPos, start.assoc);
7943
7964
  let goal = start.goalColumn, startY;
7944
7965
  let rect = view.contentDOM.getBoundingClientRect();
7945
- let startCoords = view.coordsAtPos(startPos, (start.empty ? start.assoc : 0) || (forward ? 1 : -1)), docTop = view.documentTop;
7966
+ let startCoords = view.coordsAtPos(startPos, start.assoc || ((start.empty ? forward : start.head == start.from) ? 1 : -1));
7967
+ let docTop = view.documentTop;
7946
7968
  if (startCoords) {
7947
7969
  if (goal == null)
7948
7970
  goal = startCoords.left - rect.left;
@@ -7955,9 +7977,16 @@
7955
7977
  startY = (dir < 0 ? line.top : line.bottom) + docTop;
7956
7978
  }
7957
7979
  let resolvedGoal = rect.left + goal;
7958
- let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
7959
- let pos = posAtCoords(view, { x: resolvedGoal, y: startY + dist * dir }, false, dir);
7960
- return EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
7980
+ let halfText = view.viewState.heightOracle.textHeight >> 1, dist = distance !== null && distance !== void 0 ? distance : halfText;
7981
+ for (let scan = 0;; scan += halfText) {
7982
+ let y = startY + (dist + scan) * dir;
7983
+ let pos = posAtCoords(view, { x: resolvedGoal, y }, false, dir);
7984
+ if (forward ? y > rect.bottom : y < rect.top)
7985
+ return EditorSelection.cursor(pos.pos, pos.assoc);
7986
+ let posCoords = view.coordsAtPos(pos.pos, pos.assoc), mid = posCoords ? (posCoords.top + posCoords.bottom) / 2 : 0;
7987
+ if (!posCoords || (forward ? mid > startY : mid < startY))
7988
+ return EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
7989
+ }
7961
7990
  }
7962
7991
  function skipAtomicRanges(atoms, pos, bias) {
7963
7992
  for (;;) {
@@ -8049,86 +8078,167 @@
8049
8078
  let line = view.docView.lineAt(block.from, 2);
8050
8079
  if (!line || line.length != block.length)
8051
8080
  line = view.docView.lineAt(block.from, -2);
8052
- return posAtCoordsInline(view, line, block.from, x, y);
8081
+ return new InlineCoordsScan(view, x, y, view.textDirectionAt(block.from)).scanTile(line, block.from);
8053
8082
  }
8054
- // Scan through the rectangles for the content of a tile, finding the
8055
- // one closest to the given coordinates, prefering closeness in Y over
8056
- // closeness in X.
8057
- //
8058
- // If this is a text tile, go character-by-character. For line or mark
8059
- // tiles, check each non-point-widget child, and descend text or mark
8060
- // tiles with a recursive call.
8061
- //
8062
- // For non-wrapped, purely left-to-right text, this could use a binary
8063
- // search. But because this seems to be fast enough, for how often it
8064
- // is called, there's not currently a specialized implementation for
8065
- // that.
8066
- function posAtCoordsInline(view, tile, offset, x, y) {
8067
- let closest = -1, closestRect = null;
8068
- let dxClosest = 1e9, dyClosest = 1e9;
8069
- let rowTop = y, rowBot = y;
8070
- let checkRects = (rects, index) => {
8071
- for (let i = 0; i < rects.length; i++) {
8072
- let rect = rects[i];
8073
- if (rect.top == rect.bottom)
8074
- continue;
8075
- let dx = rect.left > x ? rect.left - x : rect.right < x ? x - rect.right : 0;
8076
- let dy = rect.top > y ? rect.top - y : rect.bottom < y ? y - rect.bottom : 0;
8077
- if (rect.top <= rowBot && rect.bottom >= rowTop) {
8078
- // Rectangle is in the current row
8079
- rowTop = Math.min(rect.top, rowTop);
8080
- rowBot = Math.max(rect.bottom, rowBot);
8081
- dy = 0;
8082
- }
8083
- if (closest < 0 || (dy - dyClosest || dx - dxClosest) < 0) {
8084
- if (closest >= 0 && dyClosest && dxClosest < dx &&
8085
- closestRect.top <= rowBot - 2 && closestRect.bottom >= rowTop + 2) {
8086
- // Retroactively set dy to 0 if the current match is in this row.
8087
- dyClosest = 0;
8088
- }
8089
- else {
8090
- closest = index;
8091
- dxClosest = dx;
8092
- dyClosest = dy;
8093
- closestRect = rect;
8083
+ class InlineCoordsScan {
8084
+ constructor(view, x, y, baseDir) {
8085
+ this.view = view;
8086
+ this.x = x;
8087
+ this.y = y;
8088
+ this.baseDir = baseDir;
8089
+ // Cached bidi info
8090
+ this.line = null;
8091
+ this.spans = null;
8092
+ }
8093
+ bidiSpansAt(pos) {
8094
+ if (!this.line || this.line.from > pos || this.line.to < pos) {
8095
+ this.line = this.view.state.doc.lineAt(pos);
8096
+ this.spans = this.view.bidiSpans(this.line);
8097
+ }
8098
+ return this;
8099
+ }
8100
+ baseDirAt(pos, side) {
8101
+ let { line, spans } = this.bidiSpansAt(pos);
8102
+ let level = spans[BidiSpan.find(spans, pos - line.from, -1, side)].level;
8103
+ return level == this.baseDir;
8104
+ }
8105
+ dirAt(pos, side) {
8106
+ let { line, spans } = this.bidiSpansAt(pos);
8107
+ return spans[BidiSpan.find(spans, pos - line.from, -1, side)].dir;
8108
+ }
8109
+ // Used to short-circuit bidi tests for content with a uniform direction
8110
+ bidiIn(from, to) {
8111
+ let { spans, line } = this.bidiSpansAt(from);
8112
+ return spans.length > 1 || spans.length && (spans[0].level != this.baseDir || spans[0].to + line.from < to);
8113
+ }
8114
+ // Scan through the rectangles for the content of a tile with inline
8115
+ // content, looking for one that overlaps the queried position
8116
+ // vertically andis
8117
+ // closest horizontally. The caller is responsible for dividing its
8118
+ // content into N pieces, and pass an array with N+1 positions
8119
+ // (including the position after the last piece). For a text tile,
8120
+ // these will be character clusters, for a composite tile, these
8121
+ // will be child tiles.
8122
+ scan(positions, getRects) {
8123
+ let lo = 0, hi = positions.length - 1, seen = new Set();
8124
+ let bidi = this.bidiIn(positions[0], positions[hi]);
8125
+ let above, below;
8126
+ let closestI = -1, closestDx = 1e9, closestRect;
8127
+ // Because, when the content is bidirectional, a regular binary
8128
+ // search is hard to perform (the content order does not
8129
+ // correspond to visual order), this loop does something between a
8130
+ // regular binary search and a full scan, depending on what it can
8131
+ // get away with. The outer hi/lo bounds are only adjusted for
8132
+ // elements that are part of the base order.
8133
+ //
8134
+ // To make sure all elements inside those bounds are visited,
8135
+ // eventually, we keep a set of seen indices, and if the midpoint
8136
+ // has already been handled, we start in a random index within the
8137
+ // current bounds and scan forward until we find an index that
8138
+ // hasn't been seen yet.
8139
+ search: while (lo < hi) {
8140
+ let dist = hi - lo, mid = (lo + hi) >> 1;
8141
+ adjust: if (seen.has(mid)) {
8142
+ let scan = lo + Math.floor(Math.random() * dist);
8143
+ for (let i = 0; i < dist; i++) {
8144
+ if (!seen.has(scan)) {
8145
+ mid = scan;
8146
+ break adjust;
8147
+ }
8148
+ scan++;
8149
+ if (scan == hi)
8150
+ scan = lo; // Wrap around
8094
8151
  }
8152
+ break search; // No index found, we're done
8095
8153
  }
8154
+ seen.add(mid);
8155
+ let rects = getRects(mid);
8156
+ if (rects)
8157
+ for (let i = 0; i < rects.length; i++) {
8158
+ let rect = rects[i], side = 0;
8159
+ // Ignore empty rectangles when there are other rectangles
8160
+ if (rect.width == 0 && rects.length > 1)
8161
+ continue;
8162
+ if (rect.bottom < this.y) {
8163
+ if (!above || above.bottom < rect.bottom)
8164
+ above = rect;
8165
+ side = 1;
8166
+ }
8167
+ else if (rect.top > this.y) {
8168
+ if (!below || below.top > rect.top)
8169
+ below = rect;
8170
+ side = -1;
8171
+ }
8172
+ else {
8173
+ let off = rect.left > this.x ? this.x - rect.left : rect.right < this.x ? this.x - rect.right : 0;
8174
+ let dx = Math.abs(off);
8175
+ if (dx < closestDx) {
8176
+ closestI = mid;
8177
+ closestDx = dx;
8178
+ closestRect = rect;
8179
+ }
8180
+ if (off)
8181
+ side = (off < 0) == (this.baseDir == Direction.LTR) ? -1 : 1;
8182
+ }
8183
+ // Narrow binary search when it is safe to do so
8184
+ if (side == -1 && (!bidi || this.baseDirAt(positions[mid], 1)))
8185
+ hi = mid;
8186
+ else if (side == 1 && (!bidi || this.baseDirAt(positions[mid + 1], -1)))
8187
+ lo = mid + 1;
8188
+ }
8096
8189
  }
8097
- };
8098
- if (tile.isText()) {
8099
- for (let i = 0; i < tile.length;) {
8100
- let next = findClusterBreak(tile.text, i);
8101
- checkRects(textRange(tile.dom, i, next).getClientRects(), i);
8102
- if (!dxClosest && !dyClosest)
8103
- break;
8104
- i = next;
8190
+ // If no element with y overlap is found, find the nearest element
8191
+ // on the y axis, move this.y into it, and retry the scan.
8192
+ if (!closestRect) {
8193
+ let side = above && (!below || (this.y - above.bottom < below.top - this.y)) ? above : below;
8194
+ this.y = (side.top + side.bottom) / 2;
8195
+ return this.scan(positions, getRects);
8105
8196
  }
8106
- let after = (x > (closestRect.left + closestRect.right) / 2) == (dirAt(view, closest + offset) == Direction.LTR);
8107
- return after ? new PosAssoc(offset + findClusterBreak(tile.text, closest), -1) : new PosAssoc(offset + closest, 1);
8197
+ let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == Direction.LTR;
8198
+ return {
8199
+ i: closestI,
8200
+ // Test whether x is closes to the start or end of this element
8201
+ after: (this.x > (closestRect.left + closestRect.right) / 2) == ltr
8202
+ };
8108
8203
  }
8109
- else {
8204
+ scanText(tile, offset) {
8205
+ let positions = [];
8206
+ for (let i = 0; i < tile.length; i = findClusterBreak(tile.text, i))
8207
+ positions.push(offset + i);
8208
+ positions.push(offset + tile.length);
8209
+ let scan = this.scan(positions, i => {
8210
+ let off = positions[i] - offset, end = positions[i + 1] - offset;
8211
+ return textRange(tile.dom, off, end).getClientRects();
8212
+ });
8213
+ return scan.after ? new PosAssoc(positions[scan.i + 1], -1) : new PosAssoc(positions[scan.i], 1);
8214
+ }
8215
+ scanTile(tile, offset) {
8110
8216
  if (!tile.length)
8111
8217
  return new PosAssoc(offset, 1);
8112
- for (let i = 0; i < tile.children.length; i++) {
8218
+ if (tile.children.length == 1) { // Short-circuit single-child tiles
8219
+ let child = tile.children[0];
8220
+ if (child.isText())
8221
+ return this.scanText(child, offset);
8222
+ else if (child.isComposite())
8223
+ return this.scanTile(child, offset);
8224
+ }
8225
+ let positions = [offset];
8226
+ for (let i = 0, pos = offset; i < tile.children.length; i++)
8227
+ positions.push(pos += tile.children[i].length);
8228
+ let scan = this.scan(positions, i => {
8113
8229
  let child = tile.children[i];
8114
8230
  if (child.flags & 48 /* TileFlag.PointWidget */)
8115
- continue;
8116
- let rects = (child.dom.nodeType == 1 ? child.dom : textRange(child.dom, 0, child.length)).getClientRects();
8117
- checkRects(rects, i);
8118
- if (!dxClosest && !dyClosest)
8119
- break;
8120
- }
8121
- let inner = tile.children[closest], innerOff = tile.posBefore(inner, offset);
8122
- if (inner.isComposite() || inner.isText())
8123
- return posAtCoordsInline(view, inner, innerOff, Math.max(closestRect.left, Math.min(closestRect.right, x)), y);
8124
- let after = (x > (closestRect.left + closestRect.right) / 2) == (dirAt(view, closest + offset) == Direction.LTR);
8125
- return after ? new PosAssoc(innerOff + inner.length, -1) : new PosAssoc(innerOff, 1);
8231
+ return null;
8232
+ return (child.dom.nodeType == 1 ? child.dom : textRange(child.dom, 0, child.length)).getClientRects();
8233
+ });
8234
+ let child = tile.children[scan.i], pos = positions[scan.i];
8235
+ if (child.isText())
8236
+ return this.scanText(child, pos);
8237
+ if (child.isComposite())
8238
+ return this.scanTile(child, pos);
8239
+ return scan.after ? new PosAssoc(positions[scan.i + 1], -1) : new PosAssoc(pos, 1);
8126
8240
  }
8127
8241
  }
8128
- function dirAt(view, pos) {
8129
- let line = view.state.doc.lineAt(pos), spans = view.bidiSpans(line);
8130
- return spans[BidiSpan.find(view.bidiSpans(line), pos - line.from, -1, 1)].dir;
8131
- }
8132
8242
 
8133
8243
  const LineBreakPlaceholder = "\uffff";
8134
8244
  class DOMReader {
@@ -8272,7 +8382,7 @@
8272
8382
  this.bounds = null;
8273
8383
  this.text = "";
8274
8384
  this.domChanged = start > -1;
8275
- let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
8385
+ let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView, curSel = view.state.selection;
8276
8386
  if (view.state.readOnly && start > -1) {
8277
8387
  // Ignore changes when the editor is read-only
8278
8388
  this.newSel = null;
@@ -8288,18 +8398,18 @@
8288
8398
  let domSel = view.observer.selectionRange;
8289
8399
  let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
8290
8400
  !contains(view.contentDOM, domSel.focusNode)
8291
- ? view.state.selection.main.head
8401
+ ? curSel.main.head
8292
8402
  : view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
8293
8403
  let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
8294
8404
  !contains(view.contentDOM, domSel.anchorNode)
8295
- ? view.state.selection.main.anchor
8405
+ ? curSel.main.anchor
8296
8406
  : view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
8297
8407
  // iOS will refuse to select the block gaps when doing
8298
8408
  // select-all.
8299
8409
  // Chrome will put the selection *inside* them, confusing
8300
8410
  // posFromDOM
8301
8411
  let vp = view.viewport;
8302
- if ((browser.ios || browser.chrome) && view.state.selection.main.empty && head != anchor &&
8412
+ if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
8303
8413
  (vp.from > 0 || vp.to < view.state.doc.length)) {
8304
8414
  let from = Math.min(head, anchor), to = Math.max(head, anchor);
8305
8415
  let offFrom = vp.from - from, offTo = vp.to - to;
@@ -8308,10 +8418,22 @@
8308
8418
  anchor = view.state.doc.length;
8309
8419
  }
8310
8420
  }
8311
- if (view.inputState.composing > -1 && view.state.selection.ranges.length > 1)
8312
- this.newSel = view.state.selection.replaceRange(EditorSelection.range(anchor, head));
8313
- else
8421
+ if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
8422
+ this.newSel = curSel.replaceRange(EditorSelection.range(anchor, head));
8423
+ }
8424
+ else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
8425
+ view.inputState.lastTouchTime > Date.now() - 100) {
8426
+ // If this is a cursor selection change in a line-wrapping
8427
+ // editor that may have been a touch, use the last touch
8428
+ // position to assign a side to the cursor.
8429
+ let before = view.coordsAtPos(head, -1), assoc = 0;
8430
+ if (before)
8431
+ assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1;
8432
+ this.newSel = EditorSelection.create([EditorSelection.cursor(head, assoc)]);
8433
+ }
8434
+ else {
8314
8435
  this.newSel = EditorSelection.single(anchor, head);
8436
+ }
8315
8437
  }
8316
8438
  }
8317
8439
  }
@@ -8347,7 +8469,7 @@
8347
8469
  }
8348
8470
  function applyDOMChange(view, domChange) {
8349
8471
  let change;
8350
- let { newSel } = domChange, sel = view.state.selection.main;
8472
+ let { newSel } = domChange, { state } = view, sel = state.selection.main;
8351
8473
  let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
8352
8474
  if (domChange.bounds) {
8353
8475
  let { from, to } = domChange.bounds;
@@ -8358,8 +8480,15 @@
8358
8480
  preferredPos = sel.to;
8359
8481
  preferredSide = "end";
8360
8482
  }
8361
- let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), domChange.text, preferredPos - from, preferredSide);
8362
- if (diff) {
8483
+ let cmp = state.doc.sliceString(from, to, LineBreakPlaceholder), selEnd, diff;
8484
+ if (!sel.empty && sel.from >= from && sel.to <= to && (domChange.typeOver || cmp != domChange.text) &&
8485
+ cmp.slice(0, sel.from - from) == domChange.text.slice(0, sel.from - from) &&
8486
+ cmp.slice(sel.to - from) == domChange.text.slice(selEnd = domChange.text.length - (cmp.length - (sel.to - from)))) {
8487
+ // This looks like a selection replacement
8488
+ change = { from: sel.from, to: sel.to,
8489
+ insert: Text.of(domChange.text.slice(sel.from - from, selEnd).split(LineBreakPlaceholder)) };
8490
+ }
8491
+ else if (diff = findDiff(cmp, domChange.text, preferredPos - from, preferredSide)) {
8363
8492
  // Chrome inserts two newlines when pressing shift-enter at the
8364
8493
  // end of a line. DomChange drops one of those.
8365
8494
  if (browser.chrome && lastKey == 13 &&
@@ -8369,16 +8498,12 @@
8369
8498
  insert: Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
8370
8499
  }
8371
8500
  }
8372
- else if (newSel && (!view.hasFocus && view.state.facet(editable) || sameSelPos(newSel, sel))) {
8501
+ else if (newSel && (!view.hasFocus && state.facet(editable) || sameSelPos(newSel, sel))) {
8373
8502
  newSel = null;
8374
8503
  }
8375
8504
  if (!change && !newSel)
8376
8505
  return false;
8377
- if (!change && domChange.typeOver && !sel.empty && newSel && newSel.main.empty) {
8378
- // Heuristic to notice typing over a selected character
8379
- change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
8380
- }
8381
- else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
8506
+ if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
8382
8507
  /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") {
8383
8508
  // Detect insert-period-on-double-space Mac and Android behavior,
8384
8509
  // and transform it into a regular space insert.
@@ -8386,18 +8511,7 @@
8386
8511
  newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
8387
8512
  change = { from: change.from, to: change.to, insert: Text.of([change.insert.toString().replace(".", " ")]) };
8388
8513
  }
8389
- else if (change && change.from >= sel.from && change.to <= sel.to &&
8390
- (change.from != sel.from || change.to != sel.to) &&
8391
- (sel.to - sel.from) - (change.to - change.from) <= 4) {
8392
- // If the change is inside the selection and covers most of it,
8393
- // assume it is a selection replace (with identical characters at
8394
- // the start/end not included in the diff)
8395
- change = {
8396
- from: sel.from, to: sel.to,
8397
- insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
8398
- };
8399
- }
8400
- else if (view.state.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
8514
+ else if (state.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
8401
8515
  view.inputState.insertingTextAt > Date.now() - 50) {
8402
8516
  // For a cross-line insertion, Chrome and Safari will crudely take
8403
8517
  // the text of the line after the selection, flattening any
@@ -8406,7 +8520,7 @@
8406
8520
  // replace of the text provided by the beforeinput event.
8407
8521
  change = {
8408
8522
  from: sel.from, to: sel.to,
8409
- insert: view.state.toText(view.inputState.insertingText)
8523
+ insert: state.toText(view.inputState.insertingText)
8410
8524
  };
8411
8525
  }
8412
8526
  else if (browser.chrome && change && change.from == change.to && change.from == sel.head &&
@@ -8428,7 +8542,7 @@
8428
8542
  scrollIntoView = true;
8429
8543
  userEvent = view.inputState.lastSelectionOrigin;
8430
8544
  if (userEvent == "select.pointer")
8431
- newSel = skipAtomsForSelection(view.state.facet(atomicRanges).map(f => f(view)), newSel);
8545
+ newSel = skipAtomsForSelection(state.facet(atomicRanges).map(f => f(view)), newSel);
8432
8546
  }
8433
8547
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
8434
8548
  return true;
@@ -8606,9 +8720,12 @@
8606
8720
  this.lastKeyCode = 0;
8607
8721
  this.lastKeyTime = 0;
8608
8722
  this.lastTouchTime = 0;
8723
+ this.lastTouchX = 0;
8724
+ this.lastTouchY = 0;
8609
8725
  this.lastFocusTime = 0;
8610
8726
  this.lastScrollTop = 0;
8611
8727
  this.lastScrollLeft = 0;
8728
+ this.lastWheelEvent = 0;
8612
8729
  // On iOS, some keys need to have their default behavior happen
8613
8730
  // (after which we retroactively handle them and reset the DOM) to
8614
8731
  // avoid messing up the virtual keyboard state.
@@ -8728,9 +8845,9 @@
8728
8845
  // applyDOMChange, notify key handlers of it and reset to
8729
8846
  // the state they produce.
8730
8847
  let pending;
8731
- if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey &&
8848
+ if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey && !event.shiftKey &&
8732
8849
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
8733
- EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
8850
+ EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey)) {
8734
8851
  this.pendingIOSKey = pending || event;
8735
8852
  setTimeout(() => this.flushIOSKey(), 250);
8736
8853
  return true;
@@ -9038,6 +9155,9 @@
9038
9155
  view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
9039
9156
  view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
9040
9157
  };
9158
+ observers.wheel = observers.mousewheel = view => {
9159
+ view.inputState.lastWheelEvent = Date.now();
9160
+ };
9041
9161
  handlers.keydown = (view, event) => {
9042
9162
  view.inputState.setSelectionOrigin("select");
9043
9163
  if (event.keyCode == 27 && view.inputState.tabFocusMode != 0)
@@ -9045,8 +9165,13 @@
9045
9165
  return false;
9046
9166
  };
9047
9167
  observers.touchstart = (view, e) => {
9048
- view.inputState.lastTouchTime = Date.now();
9049
- view.inputState.setSelectionOrigin("select.pointer");
9168
+ let iState = view.inputState, touch = e.targetTouches[0];
9169
+ iState.lastTouchTime = Date.now();
9170
+ if (touch) {
9171
+ iState.lastTouchX = touch.clientX;
9172
+ iState.lastTouchY = touch.clientY;
9173
+ }
9174
+ iState.setSelectionOrigin("select.pointer");
9050
9175
  };
9051
9176
  observers.touchmove = view => {
9052
9177
  view.inputState.setSelectionOrigin("select.pointer");
@@ -9126,10 +9251,10 @@
9126
9251
  if (start.pos != cur.pos && !extend) {
9127
9252
  let startRange = rangeForClick(view, start.pos, start.assoc, type);
9128
9253
  let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
9129
- range = from < range.from ? EditorSelection.range(from, to) : EditorSelection.range(to, from);
9254
+ range = from < range.from ? EditorSelection.range(from, to, range.assoc) : EditorSelection.range(to, from, range.assoc);
9130
9255
  }
9131
9256
  if (extend)
9132
- return startSel.replaceRange(startSel.main.extend(range.from, range.to));
9257
+ return startSel.replaceRange(startSel.main.extend(range.from, range.to, range.assoc));
9133
9258
  else if (multiple && type == 1 && startSel.ranges.length > 1 && (removed = removeRangeAround(startSel, cur.pos)))
9134
9259
  return removed;
9135
9260
  else if (multiple)
@@ -9282,8 +9407,7 @@
9282
9407
  // spans multiple elements including this CodeMirror. The copy event may
9283
9408
  // bubble through CodeMirror (e.g. when CodeMirror is the first or the last
9284
9409
  // element in the selection), but we should let the parent handle it.
9285
- let domSel = getSelection(view.root);
9286
- if (domSel && !hasSelection(view.contentDOM, domSel))
9410
+ if (!hasSelection(view.contentDOM, view.observer.selectionRange))
9287
9411
  return false;
9288
9412
  let { text, ranges, linewise } = copiedRange(view.state);
9289
9413
  if (!text && !linewise)
@@ -10282,7 +10406,8 @@
10282
10406
  get estimatedHeight() { return this.vertical ? this.size : -1; }
10283
10407
  }
10284
10408
  class ViewState {
10285
- constructor(state) {
10409
+ constructor(view, state) {
10410
+ this.view = view;
10286
10411
  this.state = state;
10287
10412
  // These are contentDOM-local coordinates
10288
10413
  this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
@@ -10293,12 +10418,14 @@
10293
10418
  this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
10294
10419
  this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
10295
10420
  this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
10296
- this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
10297
- this.scrolledToBottom = false;
10298
10421
  // The CSS-transformation scale of the editor (transformed size /
10299
10422
  // concrete size)
10300
10423
  this.scaleX = 1;
10301
10424
  this.scaleY = 1;
10425
+ // Last seen vertical offset of the element at the top of the scroll
10426
+ // container, or top of the window if there's no wrapping scroller
10427
+ this.scrollOffset = 0;
10428
+ this.scrolledToBottom = false;
10302
10429
  // The vertical position (document-relative) to which to anchor the
10303
10430
  // scroll position. -1 means anchor to the end of the document.
10304
10431
  this.scrollAnchorPos = 0;
@@ -10336,6 +10463,7 @@
10336
10463
  this.updateViewportLines();
10337
10464
  this.lineGaps = this.ensureLineGaps([]);
10338
10465
  this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
10466
+ this.scrollParent = view.scrollDOM;
10339
10467
  this.computeVisibleRanges();
10340
10468
  }
10341
10469
  updateForViewport() {
@@ -10369,7 +10497,7 @@
10369
10497
  let contentChanges = update.changedRanges;
10370
10498
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
10371
10499
  let prevHeight = this.heightMap.height;
10372
- let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
10500
+ let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollOffset);
10373
10501
  clearHeightChangeFlag();
10374
10502
  this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
10375
10503
  if (this.heightMap.height != prevHeight || heightChangeFlag)
@@ -10401,12 +10529,12 @@
10401
10529
  !update.state.facet(nativeSelectionHidden))
10402
10530
  this.mustEnforceCursorAssoc = true;
10403
10531
  }
10404
- measure(view) {
10405
- let dom = view.contentDOM, style = window.getComputedStyle(dom);
10532
+ measure() {
10533
+ let { view } = this, dom = view.contentDOM, style = window.getComputedStyle(dom);
10406
10534
  let oracle = this.heightOracle;
10407
10535
  let whiteSpace = style.whiteSpace;
10408
10536
  this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
10409
- let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent;
10537
+ let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent === "refresh";
10410
10538
  let domRect = dom.getBoundingClientRect();
10411
10539
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height;
10412
10540
  this.contentDOMHeight = domRect.height;
@@ -10436,12 +10564,18 @@
10436
10564
  this.editorWidth = view.scrollDOM.clientWidth;
10437
10565
  result |= 16 /* UpdateFlag.Geometry */;
10438
10566
  }
10439
- let scrollTop = view.scrollDOM.scrollTop * this.scaleY;
10440
- if (this.scrollTop != scrollTop) {
10567
+ let scrollParent = scrollableParents(this.view.contentDOM, false).y;
10568
+ if (scrollParent != this.scrollParent) {
10569
+ this.scrollParent = scrollParent;
10570
+ this.scrollAnchorHeight = -1;
10571
+ this.scrollOffset = 0;
10572
+ }
10573
+ let scrollOffset = this.getScrollOffset();
10574
+ if (this.scrollOffset != scrollOffset) {
10441
10575
  this.scrollAnchorHeight = -1;
10442
- this.scrollTop = scrollTop;
10576
+ this.scrollOffset = scrollOffset;
10443
10577
  }
10444
- this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
10578
+ this.scrolledToBottom = isScrolledToBottom(this.scrollParent || view.win);
10445
10579
  // Pixel viewport
10446
10580
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
10447
10581
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -10718,9 +10852,14 @@
10718
10852
  this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
10719
10853
  scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType$1.ByHeight, this.heightOracle, 0, 0), this.scaler);
10720
10854
  }
10721
- scrollAnchorAt(scrollTop) {
10722
- let block = this.lineBlockAtHeight(scrollTop + 8);
10723
- return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
10855
+ getScrollOffset() {
10856
+ let base = this.scrollParent == this.view.scrollDOM ? this.scrollParent.scrollTop
10857
+ : (this.scrollParent ? this.scrollParent.getBoundingClientRect().top : 0) - this.view.contentDOM.getBoundingClientRect().top;
10858
+ return base * this.scaleY;
10859
+ }
10860
+ scrollAnchorAt(scrollOffset) {
10861
+ let block = this.lineBlockAtHeight(scrollOffset + 8);
10862
+ return block.from >= this.viewport.from || this.viewportLines[0].top - scrollOffset > 200 ? block : this.viewportLines[0];
10724
10863
  }
10725
10864
  elementAtHeight(height) {
10726
10865
  return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
@@ -10973,6 +11112,21 @@
10973
11112
  "&dark .cm-cursor": {
10974
11113
  borderLeftColor: "#ddd"
10975
11114
  },
11115
+ ".cm-selectionHandle": {
11116
+ backgroundColor: "currentColor",
11117
+ width: "1.5px"
11118
+ },
11119
+ ".cm-selectionHandle-start::before, .cm-selectionHandle-end::before": {
11120
+ content: '""',
11121
+ backgroundColor: "inherit",
11122
+ borderRadius: "50%",
11123
+ width: "8px",
11124
+ height: "8px",
11125
+ position: "absolute",
11126
+ left: "-3.25px"
11127
+ },
11128
+ ".cm-selectionHandle-start::before": { top: "-8px" },
11129
+ ".cm-selectionHandle-end::before": { bottom: "-8px" },
10976
11130
  ".cm-dropCursor": {
10977
11131
  position: "absolute"
10978
11132
  },
@@ -11967,7 +12121,7 @@
11967
12121
  ((trs) => this.update(trs));
11968
12122
  this.dispatch = this.dispatch.bind(this);
11969
12123
  this._root = (config.root || getRoot(config.parent) || document);
11970
- this.viewState = new ViewState(config.state || EditorState.create(config));
12124
+ this.viewState = new ViewState(this, config.state || EditorState.create(config));
11971
12125
  if (config.scrollTo && config.scrollTo.is(scrollIntoView$1))
11972
12126
  this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
11973
12127
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
@@ -11983,7 +12137,7 @@
11983
12137
  this.requestMeasure();
11984
12138
  if ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready)
11985
12139
  document.fonts.ready.then(() => {
11986
- this.viewState.mustMeasureContent = true;
12140
+ this.viewState.mustMeasureContent = "refresh";
11987
12141
  this.requestMeasure();
11988
12142
  });
11989
12143
  }
@@ -12122,7 +12276,7 @@
12122
12276
  try {
12123
12277
  for (let plugin of this.plugins)
12124
12278
  plugin.destroy(this);
12125
- this.viewState = new ViewState(newState);
12279
+ this.viewState = new ViewState(this, newState);
12126
12280
  this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
12127
12281
  this.pluginMap.clear();
12128
12282
  for (let plugin of this.plugins)
@@ -12201,26 +12355,26 @@
12201
12355
  if (flush)
12202
12356
  this.observer.forceFlush();
12203
12357
  let updated = null;
12204
- let sDOM = this.scrollDOM, scrollTop = sDOM.scrollTop * this.scaleY;
12358
+ let scroll = this.viewState.scrollParent, scrollOffset = this.viewState.getScrollOffset();
12205
12359
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
12206
- if (Math.abs(scrollTop - this.viewState.scrollTop) > 1)
12360
+ if (Math.abs(scrollOffset - this.viewState.scrollOffset) > 1)
12207
12361
  scrollAnchorHeight = -1;
12208
12362
  this.viewState.scrollAnchorHeight = -1;
12209
12363
  try {
12210
12364
  for (let i = 0;; i++) {
12211
12365
  if (scrollAnchorHeight < 0) {
12212
- if (isScrolledToBottom(sDOM)) {
12366
+ if (isScrolledToBottom(scroll || this.win)) {
12213
12367
  scrollAnchorPos = -1;
12214
12368
  scrollAnchorHeight = this.viewState.heightMap.height;
12215
12369
  }
12216
12370
  else {
12217
- let block = this.viewState.scrollAnchorAt(scrollTop);
12371
+ let block = this.viewState.scrollAnchorAt(scrollOffset);
12218
12372
  scrollAnchorPos = block.from;
12219
12373
  scrollAnchorHeight = block.top;
12220
12374
  }
12221
12375
  }
12222
12376
  this.updateState = 1 /* UpdateState.Measuring */;
12223
- let changed = this.viewState.measure(this);
12377
+ let changed = this.viewState.measure();
12224
12378
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
12225
12379
  break;
12226
12380
  if (i > 5) {
@@ -12281,10 +12435,15 @@
12281
12435
  else {
12282
12436
  let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
12283
12437
  this.viewState.lineBlockAt(scrollAnchorPos).top;
12284
- let diff = newAnchorHeight - scrollAnchorHeight;
12285
- if (diff > 1 || diff < -1) {
12286
- scrollTop = scrollTop + diff;
12287
- sDOM.scrollTop = scrollTop / this.scaleY;
12438
+ let diff = (newAnchorHeight - scrollAnchorHeight) / this.scaleY;
12439
+ if ((diff > 1 || diff < -1) &&
12440
+ (scroll == this.scrollDOM || this.hasFocus ||
12441
+ Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
12442
+ scrollOffset = scrollOffset + diff;
12443
+ if (scroll)
12444
+ scroll.scrollTop += diff;
12445
+ else
12446
+ this.win.scrollBy(0, diff);
12288
12447
  scrollAnchorHeight = -1;
12289
12448
  continue;
12290
12449
  }
@@ -13532,7 +13691,8 @@
13532
13691
  combine(configs) {
13533
13692
  return combineConfig(configs, {
13534
13693
  cursorBlinkRate: 1200,
13535
- drawRangeCursor: true
13694
+ drawRangeCursor: true,
13695
+ iosSelectionHandles: true
13536
13696
  }, {
13537
13697
  cursorBlinkRate: (a, b) => Math.min(a, b),
13538
13698
  drawRangeCursor: (a, b) => a || b
@@ -13576,9 +13736,9 @@
13576
13736
  let cursors = [];
13577
13737
  for (let r of state.selection.ranges) {
13578
13738
  let prim = r == state.selection.main;
13579
- if (r.empty || conf.drawRangeCursor) {
13739
+ if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
13580
13740
  let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
13581
- let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
13741
+ let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.assoc);
13582
13742
  for (let piece of RectangleMarker.forRange(view, className, cursor))
13583
13743
  cursors.push(piece);
13584
13744
  }
@@ -13604,8 +13764,19 @@
13604
13764
  const selectionLayer = /*@__PURE__*/layer({
13605
13765
  above: false,
13606
13766
  markers(view) {
13607
- return view.state.selection.ranges.map(r => r.empty ? [] : RectangleMarker.forRange(view, "cm-selectionBackground", r))
13608
- .reduce((a, b) => a.concat(b));
13767
+ let markers = [], { main, ranges } = view.state.selection;
13768
+ for (let r of ranges)
13769
+ if (!r.empty) {
13770
+ for (let marker of RectangleMarker.forRange(view, "cm-selectionBackground", r))
13771
+ markers.push(marker);
13772
+ }
13773
+ if (browser.ios && !main.empty && view.state.facet(selectionConfig).iosSelectionHandles) {
13774
+ for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-start", EditorSelection.cursor(main.from, 1)))
13775
+ markers.push(piece);
13776
+ for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-end", EditorSelection.cursor(main.to, 1)))
13777
+ markers.push(piece);
13778
+ }
13779
+ return markers;
13609
13780
  },
13610
13781
  update(update, dom) {
13611
13782
  return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
@@ -19937,30 +20108,44 @@
19937
20108
  decorations.push(mark.range(match.end.from, match.end.to));
19938
20109
  return decorations;
19939
20110
  }
19940
- const bracketMatchingState = /*@__PURE__*/StateField.define({
19941
- create() { return Decoration.none; },
19942
- update(deco, tr) {
19943
- if (!tr.docChanged && !tr.selection)
19944
- return deco;
19945
- let decorations = [];
19946
- let config = tr.state.facet(bracketMatchingConfig);
19947
- for (let range of tr.state.selection.ranges) {
19948
- if (!range.empty)
19949
- continue;
19950
- let match = matchBrackets(tr.state, range.head, -1, config)
19951
- || (range.head > 0 && matchBrackets(tr.state, range.head - 1, 1, config))
19952
- || (config.afterCursor &&
19953
- (matchBrackets(tr.state, range.head, 1, config) ||
19954
- (range.head < tr.state.doc.length && matchBrackets(tr.state, range.head + 1, -1, config))));
19955
- if (match)
19956
- decorations = decorations.concat(config.renderMatch(match, tr.state));
19957
- }
19958
- return Decoration.set(decorations, true);
19959
- },
19960
- provide: f => EditorView.decorations.from(f)
20111
+ function bracketDeco(state) {
20112
+ let decorations = [];
20113
+ let config = state.facet(bracketMatchingConfig);
20114
+ for (let range of state.selection.ranges) {
20115
+ if (!range.empty)
20116
+ continue;
20117
+ let match = matchBrackets(state, range.head, -1, config)
20118
+ || (range.head > 0 && matchBrackets(state, range.head - 1, 1, config))
20119
+ || (config.afterCursor &&
20120
+ (matchBrackets(state, range.head, 1, config) ||
20121
+ (range.head < state.doc.length && matchBrackets(state, range.head + 1, -1, config))));
20122
+ if (match)
20123
+ decorations = decorations.concat(config.renderMatch(match, state));
20124
+ }
20125
+ return Decoration.set(decorations, true);
20126
+ }
20127
+ const bracketMatcher = /*@__PURE__*/ViewPlugin.fromClass(class {
20128
+ constructor(view) {
20129
+ this.paused = false;
20130
+ this.decorations = bracketDeco(view.state);
20131
+ }
20132
+ update(update) {
20133
+ if (update.docChanged || update.selectionSet || this.paused) {
20134
+ if (update.view.composing) {
20135
+ this.decorations = this.decorations.map(update.changes);
20136
+ this.paused = true;
20137
+ }
20138
+ else {
20139
+ this.decorations = bracketDeco(update.state);
20140
+ this.paused = false;
20141
+ }
20142
+ }
20143
+ }
20144
+ }, {
20145
+ decorations: v => v.decorations
19961
20146
  });
19962
20147
  const bracketMatchingUnique = [
19963
- bracketMatchingState,
20148
+ bracketMatcher,
19964
20149
  baseTheme$3
19965
20150
  ];
19966
20151
  /**
@@ -20861,7 +21046,7 @@
20861
21046
  function extendSel(target, how) {
20862
21047
  let selection = updateSel(target.state.selection, range => {
20863
21048
  let head = how(range);
20864
- return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined);
21049
+ return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined, head.assoc);
20865
21050
  });
20866
21051
  if (selection.eq(target.state.selection))
20867
21052
  return false;
@@ -23231,6 +23416,7 @@
23231
23416
  };
23232
23417
  }
23233
23418
 
23419
+ const setSelectedEffect = /*@__PURE__*/StateEffect.define();
23234
23420
  function optionContent(config) {
23235
23421
  let content = config.addToOptions.slice();
23236
23422
  if (config.icons)
@@ -23322,6 +23508,16 @@
23322
23508
  return;
23323
23509
  }
23324
23510
  }
23511
+ if (e.target == this.list) {
23512
+ let move = this.list.classList.contains("cm-completionListIncompleteTop") &&
23513
+ e.clientY < this.list.firstChild.getBoundingClientRect().top ? this.range.from - 1 :
23514
+ this.list.classList.contains("cm-completionListIncompleteBottom") &&
23515
+ e.clientY > this.list.lastChild.getBoundingClientRect().bottom ? this.range.to : null;
23516
+ if (move != null) {
23517
+ view.dispatch({ effects: setSelectedEffect.of(move) });
23518
+ e.preventDefault();
23519
+ }
23520
+ }
23325
23521
  });
23326
23522
  this.dom.addEventListener("focusout", (e) => {
23327
23523
  let state = view.state.field(this.stateField, false);
@@ -23823,7 +24019,6 @@
23823
24019
  const setActiveEffect = /*@__PURE__*/StateEffect.define({
23824
24020
  map(sources, mapping) { return sources.map(s => s.map(mapping)); }
23825
24021
  });
23826
- const setSelectedEffect = /*@__PURE__*/StateEffect.define();
23827
24022
  const completionState = /*@__PURE__*/StateField.define({
23828
24023
  create() { return CompletionState.start(); },
23829
24024
  update(value, tr) { return value.update(tr); },
@@ -24978,10 +25173,10 @@
24978
25173
  let field = view.state.field(lintState, false);
24979
25174
  if (!field)
24980
25175
  return false;
24981
- let sel = view.state.selection.main, next = field.diagnostics.iter(sel.to + 1);
24982
- if (!next.value) {
24983
- next = field.diagnostics.iter(0);
24984
- if (!next.value || next.from == sel.from && next.to == sel.to)
25176
+ let sel = view.state.selection.main, next = findDiagnostic(field.diagnostics, null, sel.to + 1);
25177
+ if (!next) {
25178
+ next = findDiagnostic(field.diagnostics, null, 0);
25179
+ if (!next || next.from == sel.from && next.to == sel.to)
24985
25180
  return false;
24986
25181
  }
24987
25182
  view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
@@ -25353,6 +25548,12 @@
25353
25548
  padding: 0,
25354
25549
  margin: 0
25355
25550
  }
25551
+ },
25552
+ "&dark .cm-lintRange-active": { backgroundColor: "#86714a80" },
25553
+ "&dark .cm-panel.cm-panel-lint ul": {
25554
+ "& [aria-selected]": {
25555
+ backgroundColor: "#2e343e",
25556
+ },
25356
25557
  }
25357
25558
  });
25358
25559
  function severityWeight(sev) {
@@ -27725,7 +27926,21 @@
27725
27926
  }),
27726
27927
  /*@__PURE__*/foldNodeProp.add({
27727
27928
  "Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType": foldInside,
27728
- BlockComment(tree) { return { from: tree.from + 2, to: tree.to - 2 }; }
27929
+ BlockComment(tree) { return { from: tree.from + 2, to: tree.to - 2 }; },
27930
+ JSXElement(tree) {
27931
+ let open = tree.firstChild;
27932
+ if (!open || open.name == "JSXSelfClosingTag")
27933
+ return null;
27934
+ let close = tree.lastChild;
27935
+ return { from: open.to, to: close.type.isError ? tree.to : close.from };
27936
+ },
27937
+ "JSXSelfClosingTag JSXOpenTag"(tree) {
27938
+ var _a;
27939
+ let name = (_a = tree.firstChild) === null || _a === void 0 ? void 0 : _a.nextSibling, close = tree.lastChild;
27940
+ if (!name || name.type.isError)
27941
+ return null;
27942
+ return { from: name.to, to: close.type.isError ? tree.to : close.from };
27943
+ }
27729
27944
  })
27730
27945
  ]
27731
27946
  }),