datafolder 1.0.6 → 1.0.8

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 (159) hide show
  1. package/README.md +7 -2
  2. package/index.js +1 -1
  3. package/internal.md +121 -0
  4. package/package.json +8 -6
  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 -0
  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 +27 -16
  29. package/src/QyCache.d.ts +44 -0
  30. package/src/QyCache.js +71 -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 +3 -4
  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 +30 -20
  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 +11 -13
  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 +21 -22
  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 +56 -53
  94. package/src/QySnapshots.d.ts +27 -0
  95. package/src/QySnapshots.js +74 -66
  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/rpc/getTriggerFiles.js +1 -0
  110. package/src/simulate.d.ts +1 -0
  111. package/src/simulate2.d.ts +1 -0
  112. package/src/soc_pokec.d.ts +1 -0
  113. package/src/soc_pokec.js +7 -7
  114. package/static/codemirror.bundle/editor.bundle.js +370 -166
  115. package/static/datafolderui/QyStyledElement.js +8 -17
  116. package/static/datafolderui/QyUIFolderViewer.js +230 -0
  117. package/static/datafolderui/QyUIUtils.js +5 -5
  118. package/static/datafolderui/code-mirror.js +7 -7
  119. package/static/datafolderui/display-tree.js +22 -58
  120. package/static/datafolderui/explore-tree.js +10 -10
  121. package/static/datafolderui/folder-tree.js +71 -2
  122. package/static/datafolderui/hscrpt.js +26 -7
  123. package/static/datafolderui/json-view.js +34 -3
  124. package/static/datafolderui/main-page.js +67 -36
  125. package/static/datafolderui/query-editor.js +9 -9
  126. package/static/datafolderui/rpc-editor.js +31 -0
  127. package/static/datafolderui/trigger-editor.js +33 -0
  128. package/data/sampleDataFolder/qykv/history/acl/1_acl.txt +0 -1
  129. package/data/sampleDataFolder/qykv/history/acl/2_acl.txt +0 -1
  130. package/data/sampleDataFolder/qykv/history/acl/3_acl.txt +0 -1
  131. package/data/sampleDataFolder/qykv/history/acl/4_acl.txt +0 -1
  132. package/data/sampleDataFolder/qykv/history/acl/5_acl.txt +0 -1
  133. package/data/sampleDataFolder/qykv/snapshot/1/1_pM.txt +0 -0
  134. package/data/sampleDataFolder/qykv/snapshot/1/1_prefixPM.txt +0 -0
  135. package/data/sampleDataFolder/qykv/snapshot/1/1_vL.txt +0 -10
  136. package/data/sampleDataFolder/qykv/snapshot/2/1_pM.txt +0 -0
  137. package/data/sampleDataFolder/qykv/snapshot/2/1_prefixPM.txt +0 -0
  138. package/data/sampleDataFolder/qykv/snapshot/2/1_vL.txt +0 -1
  139. package/data/sampleDataFolder/qykv/snapshot/3/1_pM.txt +0 -0
  140. package/data/sampleDataFolder/qykv/snapshot/3/1_prefixPM.txt +0 -0
  141. package/data/sampleDataFolder/qykv/snapshot/3/1_vL.txt +0 -1
  142. package/data/sampleDataFolder/qykv/snapshot/4/1_pM.txt +0 -0
  143. package/data/sampleDataFolder/qykv/snapshot/4/1_prefixPM.txt +0 -0
  144. package/data/sampleDataFolder/qykv/snapshot/4/1_vL.txt +0 -1
  145. package/data/sampleDataFolder/qykv/snapshot/4/2_pM.txt +0 -0
  146. package/data/sampleDataFolder/qykv/snapshot/4/2_prefixPM.txt +0 -0
  147. package/data/sampleDataFolder/qykv/snapshot/4/2_vL.txt +0 -9
  148. package/data/sampleDataFolder/qykv/snapshot/5/1_pM.txt +0 -0
  149. package/data/sampleDataFolder/qykv/snapshot/5/1_prefixPM.txt +0 -0
  150. package/data/sampleDataFolder/qykv/snapshot/5/1_vL.txt +0 -55
  151. package/data/sampleDataFolder/qykv/snapshot/info/1.json +0 -1
  152. package/data/sampleDataFolder/qykv/snapshot/info/2.json +0 -1
  153. package/data/sampleDataFolder/qykv/snapshot/info/3.json +0 -1
  154. package/data/sampleDataFolder/qykv/snapshot/info/4.json +0 -1
  155. package/data/sampleDataFolder/qykv/snapshot/info/5.json +0 -1
  156. package/data/sampleDataFolder/qylog/2026/02/15/08/change.txt +0 -58
  157. package/data/sampleDataFolder/qylog/2026/02/15/08/stderr.txt +0 -11
  158. package/data/sampleDataFolder/qylog/2026/02/15/08/stdout.txt +0 -36
  159. package/static/datafolderui/QyUIFolderTree.js +0 -179
@@ -4907,16 +4907,16 @@
4907
4907
  }
4908
4908
  }
4909
4909
  }
4910
- function scrollableParents(dom) {
4911
- let doc = dom.ownerDocument, x, y;
4910
+ function scrollableParents(dom, getX = true) {
4911
+ let doc = dom.ownerDocument, x = null, y = null;
4912
4912
  for (let cur = dom.parentNode; cur;) {
4913
- if (cur == doc.body || (x && y)) {
4913
+ if (cur == doc.body || ((!getX || x) && y)) {
4914
4914
  break;
4915
4915
  }
4916
4916
  else if (cur.nodeType == 1) {
4917
4917
  if (!y && cur.scrollHeight > cur.clientHeight)
4918
4918
  y = cur;
4919
- if (!x && cur.scrollWidth > cur.clientWidth)
4919
+ if (getX && !x && cur.scrollWidth > cur.clientWidth)
4920
4920
  x = cur;
4921
4921
  cur = cur.assignedSlot || cur.parentNode;
4922
4922
  }
@@ -5041,6 +5041,8 @@
5041
5041
  }
5042
5042
  }
5043
5043
  function isScrolledToBottom(elt) {
5044
+ if (elt instanceof Window)
5045
+ return elt.pageYOffset > Math.max(0, elt.document.documentElement.scrollHeight - elt.innerHeight - 4);
5044
5046
  return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
5045
5047
  }
5046
5048
  function textNodeBefore(startNode, startOffset) {
@@ -6938,7 +6940,7 @@
6938
6940
  }
6939
6941
  else if (tile.isText()) {
6940
6942
  this.builder.ensureLine(null);
6941
- if (!from && to == tile.length) {
6943
+ if (!from && to == tile.length && !this.cache.reused.has(tile)) {
6942
6944
  this.builder.addText(tile.text, activeMarks, openMarks, this.cache.reuse(tile));
6943
6945
  }
6944
6946
  else {
@@ -7073,9 +7075,10 @@
7073
7075
  marks.push(tile);
7074
7076
  else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
7075
7077
  line = tile;
7078
+ else if (tile instanceof BlockWrapperTile) ; // Ignore
7076
7079
  else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
7077
7080
  line = new LineTile(parent, lineBaseAttrs);
7078
- else
7081
+ else if (!line)
7079
7082
  marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
7080
7083
  }
7081
7084
  return { line: line, marks };
@@ -7241,6 +7244,8 @@
7241
7244
  if (composition || changes.length) {
7242
7245
  let oldTile = this.tile;
7243
7246
  let builder = new TileUpdate(this.view, oldTile, this.blockWrappers, this.decorations, this.dynamicDecorationMap);
7247
+ if (composition && Tile.get(composition.text))
7248
+ builder.cache.reused.set(Tile.get(composition.text), 2 /* Reused.DOM */);
7244
7249
  this.tile = builder.run(changes, composition);
7245
7250
  destroyDropped(oldTile, builder.cache.reused);
7246
7251
  }
@@ -7701,6 +7706,19 @@
7701
7706
  };
7702
7707
  let { offsetWidth, offsetHeight } = this.view.scrollDOM;
7703
7708
  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);
7709
+ // On mobile browsers, the visual viewport may be smaller than the
7710
+ // actual reported viewport, causing scrollRectIntoView to fail to
7711
+ // scroll properly. Unfortunately, this visual viewport cannot be
7712
+ // updated directly, and scrollIntoView is the only way a script
7713
+ // can affect it. So this tries to kludge around the problem by
7714
+ // calling scrollIntoView on the scroll target's line.
7715
+ if (window.visualViewport && window.innerHeight - window.visualViewport.height > 1 &&
7716
+ (rect.top > window.pageYOffset + window.visualViewport.offsetTop + window.visualViewport.height ||
7717
+ rect.bottom < window.pageYOffset + window.visualViewport.offsetTop)) {
7718
+ let line = this.view.docView.lineAt(range.head, 1);
7719
+ if (line)
7720
+ line.dom.scrollIntoView({ block: "nearest" });
7721
+ }
7704
7722
  }
7705
7723
  lineHasWidget(pos) {
7706
7724
  let scan = (child) => child.isWidget() || child.children.some(scan);
@@ -8049,86 +8067,167 @@
8049
8067
  let line = view.docView.lineAt(block.from, 2);
8050
8068
  if (!line || line.length != block.length)
8051
8069
  line = view.docView.lineAt(block.from, -2);
8052
- return posAtCoordsInline(view, line, block.from, x, y);
8070
+ return new InlineCoordsScan(view, x, y, view.textDirectionAt(block.from)).scanTile(line, block.from);
8053
8071
  }
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;
8072
+ class InlineCoordsScan {
8073
+ constructor(view, x, y, baseDir) {
8074
+ this.view = view;
8075
+ this.x = x;
8076
+ this.y = y;
8077
+ this.baseDir = baseDir;
8078
+ // Cached bidi info
8079
+ this.line = null;
8080
+ this.spans = null;
8081
+ }
8082
+ bidiSpansAt(pos) {
8083
+ if (!this.line || this.line.from > pos || this.line.to < pos) {
8084
+ this.line = this.view.state.doc.lineAt(pos);
8085
+ this.spans = this.view.bidiSpans(this.line);
8086
+ }
8087
+ return this;
8088
+ }
8089
+ baseDirAt(pos, side) {
8090
+ let { line, spans } = this.bidiSpansAt(pos);
8091
+ let level = spans[BidiSpan.find(spans, pos - line.from, -1, side)].level;
8092
+ return level == this.baseDir;
8093
+ }
8094
+ dirAt(pos, side) {
8095
+ let { line, spans } = this.bidiSpansAt(pos);
8096
+ return spans[BidiSpan.find(spans, pos - line.from, -1, side)].dir;
8097
+ }
8098
+ // Used to short-circuit bidi tests for content with a uniform direction
8099
+ bidiIn(from, to) {
8100
+ let { spans, line } = this.bidiSpansAt(from);
8101
+ return spans.length > 1 || spans.length && (spans[0].level != this.baseDir || spans[0].to + line.from < to);
8102
+ }
8103
+ // Scan through the rectangles for the content of a tile with inline
8104
+ // content, looking for one that overlaps the queried position
8105
+ // vertically andis
8106
+ // closest horizontally. The caller is responsible for dividing its
8107
+ // content into N pieces, and pass an array with N+1 positions
8108
+ // (including the position after the last piece). For a text tile,
8109
+ // these will be character clusters, for a composite tile, these
8110
+ // will be child tiles.
8111
+ scan(positions, getRects) {
8112
+ let lo = 0, hi = positions.length - 1, seen = new Set();
8113
+ let bidi = this.bidiIn(positions[0], positions[hi]);
8114
+ let above, below;
8115
+ let closestI = -1, closestDx = 1e9, closestRect;
8116
+ // Because, when the content is bidirectional, a regular binary
8117
+ // search is hard to perform (the content order does not
8118
+ // correspond to visual order), this loop does something between a
8119
+ // regular binary search and a full scan, depending on what it can
8120
+ // get away with. The outer hi/lo bounds are only adjusted for
8121
+ // elements that are part of the base order.
8122
+ //
8123
+ // To make sure all elements inside those bounds are visited,
8124
+ // eventually, we keep a set of seen indices, and if the midpoint
8125
+ // has already been handled, we start in a random index within the
8126
+ // current bounds and scan forward until we find an index that
8127
+ // hasn't been seen yet.
8128
+ search: while (lo < hi) {
8129
+ let dist = hi - lo, mid = (lo + hi) >> 1;
8130
+ adjust: if (seen.has(mid)) {
8131
+ let scan = lo + Math.floor(Math.random() * dist);
8132
+ for (let i = 0; i < dist; i++) {
8133
+ if (!seen.has(scan)) {
8134
+ mid = scan;
8135
+ break adjust;
8136
+ }
8137
+ scan++;
8138
+ if (scan == hi)
8139
+ scan = lo; // Wrap around
8094
8140
  }
8141
+ break search; // No index found, we're done
8095
8142
  }
8143
+ seen.add(mid);
8144
+ let rects = getRects(mid);
8145
+ if (rects)
8146
+ for (let i = 0; i < rects.length; i++) {
8147
+ let rect = rects[i], side = 0;
8148
+ // Ignore empty rectangles when there are other rectangles
8149
+ if (rect.width == 0 && rects.length > 1)
8150
+ continue;
8151
+ if (rect.bottom < this.y) {
8152
+ if (!above || above.bottom < rect.bottom)
8153
+ above = rect;
8154
+ side = 1;
8155
+ }
8156
+ else if (rect.top > this.y) {
8157
+ if (!below || below.top > rect.top)
8158
+ below = rect;
8159
+ side = -1;
8160
+ }
8161
+ else {
8162
+ let off = rect.left > this.x ? this.x - rect.left : rect.right < this.x ? this.x - rect.right : 0;
8163
+ let dx = Math.abs(off);
8164
+ if (dx < closestDx) {
8165
+ closestI = mid;
8166
+ closestDx = dx;
8167
+ closestRect = rect;
8168
+ }
8169
+ if (off)
8170
+ side = (off < 0) == (this.baseDir == Direction.LTR) ? -1 : 1;
8171
+ }
8172
+ // Narrow binary search when it is safe to do so
8173
+ if (side == -1 && (!bidi || this.baseDirAt(positions[mid], 1)))
8174
+ hi = mid;
8175
+ else if (side == 1 && (!bidi || this.baseDirAt(positions[mid + 1], -1)))
8176
+ lo = mid + 1;
8177
+ }
8096
8178
  }
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;
8179
+ // If no element with y overlap is found, find the nearest element
8180
+ // on the y axis, move this.y into it, and retry the scan.
8181
+ if (!closestRect) {
8182
+ let side = above && (!below || (this.y - above.bottom < below.top - this.y)) ? above : below;
8183
+ this.y = (side.top + side.bottom) / 2;
8184
+ return this.scan(positions, getRects);
8105
8185
  }
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);
8186
+ let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == Direction.LTR;
8187
+ return {
8188
+ i: closestI,
8189
+ // Test whether x is closes to the start or end of this element
8190
+ after: (this.x > (closestRect.left + closestRect.right) / 2) == ltr
8191
+ };
8108
8192
  }
8109
- else {
8193
+ scanText(tile, offset) {
8194
+ let positions = [];
8195
+ for (let i = 0; i < tile.length; i = findClusterBreak(tile.text, i))
8196
+ positions.push(offset + i);
8197
+ positions.push(offset + tile.length);
8198
+ let scan = this.scan(positions, i => {
8199
+ let off = positions[i] - offset, end = positions[i + 1] - offset;
8200
+ return textRange(tile.dom, off, end).getClientRects();
8201
+ });
8202
+ return scan.after ? new PosAssoc(positions[scan.i + 1], -1) : new PosAssoc(positions[scan.i], 1);
8203
+ }
8204
+ scanTile(tile, offset) {
8110
8205
  if (!tile.length)
8111
8206
  return new PosAssoc(offset, 1);
8112
- for (let i = 0; i < tile.children.length; i++) {
8207
+ if (tile.children.length == 1) { // Short-circuit single-child tiles
8208
+ let child = tile.children[0];
8209
+ if (child.isText())
8210
+ return this.scanText(child, offset);
8211
+ else if (child.isComposite())
8212
+ return this.scanTile(child, offset);
8213
+ }
8214
+ let positions = [offset];
8215
+ for (let i = 0, pos = offset; i < tile.children.length; i++)
8216
+ positions.push(pos += tile.children[i].length);
8217
+ let scan = this.scan(positions, i => {
8113
8218
  let child = tile.children[i];
8114
8219
  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);
8220
+ return null;
8221
+ return (child.dom.nodeType == 1 ? child.dom : textRange(child.dom, 0, child.length)).getClientRects();
8222
+ });
8223
+ let child = tile.children[scan.i], pos = positions[scan.i];
8224
+ if (child.isText())
8225
+ return this.scanText(child, pos);
8226
+ if (child.isComposite())
8227
+ return this.scanTile(child, pos);
8228
+ return scan.after ? new PosAssoc(positions[scan.i + 1], -1) : new PosAssoc(pos, 1);
8126
8229
  }
8127
8230
  }
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
8231
 
8133
8232
  const LineBreakPlaceholder = "\uffff";
8134
8233
  class DOMReader {
@@ -8272,7 +8371,7 @@
8272
8371
  this.bounds = null;
8273
8372
  this.text = "";
8274
8373
  this.domChanged = start > -1;
8275
- let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
8374
+ let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView, curSel = view.state.selection;
8276
8375
  if (view.state.readOnly && start > -1) {
8277
8376
  // Ignore changes when the editor is read-only
8278
8377
  this.newSel = null;
@@ -8288,18 +8387,18 @@
8288
8387
  let domSel = view.observer.selectionRange;
8289
8388
  let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
8290
8389
  !contains(view.contentDOM, domSel.focusNode)
8291
- ? view.state.selection.main.head
8390
+ ? curSel.main.head
8292
8391
  : view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
8293
8392
  let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
8294
8393
  !contains(view.contentDOM, domSel.anchorNode)
8295
- ? view.state.selection.main.anchor
8394
+ ? curSel.main.anchor
8296
8395
  : view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
8297
8396
  // iOS will refuse to select the block gaps when doing
8298
8397
  // select-all.
8299
8398
  // Chrome will put the selection *inside* them, confusing
8300
8399
  // posFromDOM
8301
8400
  let vp = view.viewport;
8302
- if ((browser.ios || browser.chrome) && view.state.selection.main.empty && head != anchor &&
8401
+ if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
8303
8402
  (vp.from > 0 || vp.to < view.state.doc.length)) {
8304
8403
  let from = Math.min(head, anchor), to = Math.max(head, anchor);
8305
8404
  let offFrom = vp.from - from, offTo = vp.to - to;
@@ -8308,10 +8407,22 @@
8308
8407
  anchor = view.state.doc.length;
8309
8408
  }
8310
8409
  }
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
8410
+ if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
8411
+ this.newSel = curSel.replaceRange(EditorSelection.range(anchor, head));
8412
+ }
8413
+ else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
8414
+ view.inputState.lastTouchTime > Date.now() - 100) {
8415
+ // If this is a cursor selection change in a line-wrapping
8416
+ // editor that may have been a touch, use the last touch
8417
+ // position to assign a side to the cursor.
8418
+ let before = view.coordsAtPos(head, -1), assoc = 0;
8419
+ if (before)
8420
+ assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1;
8421
+ this.newSel = EditorSelection.create([EditorSelection.cursor(head, assoc)]);
8422
+ }
8423
+ else {
8314
8424
  this.newSel = EditorSelection.single(anchor, head);
8425
+ }
8315
8426
  }
8316
8427
  }
8317
8428
  }
@@ -8347,7 +8458,7 @@
8347
8458
  }
8348
8459
  function applyDOMChange(view, domChange) {
8349
8460
  let change;
8350
- let { newSel } = domChange, sel = view.state.selection.main;
8461
+ let { newSel } = domChange, { state } = view, sel = state.selection.main;
8351
8462
  let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1;
8352
8463
  if (domChange.bounds) {
8353
8464
  let { from, to } = domChange.bounds;
@@ -8358,8 +8469,15 @@
8358
8469
  preferredPos = sel.to;
8359
8470
  preferredSide = "end";
8360
8471
  }
8361
- let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), domChange.text, preferredPos - from, preferredSide);
8362
- if (diff) {
8472
+ let cmp = state.doc.sliceString(from, to, LineBreakPlaceholder), selEnd, diff;
8473
+ if (!sel.empty && sel.from >= from && sel.to <= to && (domChange.typeOver || cmp != domChange.text) &&
8474
+ cmp.slice(0, sel.from - from) == domChange.text.slice(0, sel.from - from) &&
8475
+ cmp.slice(sel.to - from) == domChange.text.slice(selEnd = domChange.text.length - (cmp.length - (sel.to - from)))) {
8476
+ // This looks like a selection replacement
8477
+ change = { from: sel.from, to: sel.to,
8478
+ insert: Text.of(domChange.text.slice(sel.from - from, selEnd).split(LineBreakPlaceholder)) };
8479
+ }
8480
+ else if (diff = findDiff(cmp, domChange.text, preferredPos - from, preferredSide)) {
8363
8481
  // Chrome inserts two newlines when pressing shift-enter at the
8364
8482
  // end of a line. DomChange drops one of those.
8365
8483
  if (browser.chrome && lastKey == 13 &&
@@ -8369,16 +8487,12 @@
8369
8487
  insert: Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
8370
8488
  }
8371
8489
  }
8372
- else if (newSel && (!view.hasFocus && view.state.facet(editable) || sameSelPos(newSel, sel))) {
8490
+ else if (newSel && (!view.hasFocus && state.facet(editable) || sameSelPos(newSel, sel))) {
8373
8491
  newSel = null;
8374
8492
  }
8375
8493
  if (!change && !newSel)
8376
8494
  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 &&
8495
+ if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
8382
8496
  /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") {
8383
8497
  // Detect insert-period-on-double-space Mac and Android behavior,
8384
8498
  // and transform it into a regular space insert.
@@ -8386,18 +8500,7 @@
8386
8500
  newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
8387
8501
  change = { from: change.from, to: change.to, insert: Text.of([change.insert.toString().replace(".", " ")]) };
8388
8502
  }
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) &&
8503
+ else if (state.doc.lineAt(sel.from).to < sel.to && view.docView.lineHasWidget(sel.to) &&
8401
8504
  view.inputState.insertingTextAt > Date.now() - 50) {
8402
8505
  // For a cross-line insertion, Chrome and Safari will crudely take
8403
8506
  // the text of the line after the selection, flattening any
@@ -8406,7 +8509,7 @@
8406
8509
  // replace of the text provided by the beforeinput event.
8407
8510
  change = {
8408
8511
  from: sel.from, to: sel.to,
8409
- insert: view.state.toText(view.inputState.insertingText)
8512
+ insert: state.toText(view.inputState.insertingText)
8410
8513
  };
8411
8514
  }
8412
8515
  else if (browser.chrome && change && change.from == change.to && change.from == sel.head &&
@@ -8428,7 +8531,7 @@
8428
8531
  scrollIntoView = true;
8429
8532
  userEvent = view.inputState.lastSelectionOrigin;
8430
8533
  if (userEvent == "select.pointer")
8431
- newSel = skipAtomsForSelection(view.state.facet(atomicRanges).map(f => f(view)), newSel);
8534
+ newSel = skipAtomsForSelection(state.facet(atomicRanges).map(f => f(view)), newSel);
8432
8535
  }
8433
8536
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
8434
8537
  return true;
@@ -8606,9 +8709,12 @@
8606
8709
  this.lastKeyCode = 0;
8607
8710
  this.lastKeyTime = 0;
8608
8711
  this.lastTouchTime = 0;
8712
+ this.lastTouchX = 0;
8713
+ this.lastTouchY = 0;
8609
8714
  this.lastFocusTime = 0;
8610
8715
  this.lastScrollTop = 0;
8611
8716
  this.lastScrollLeft = 0;
8717
+ this.lastWheelEvent = 0;
8612
8718
  // On iOS, some keys need to have their default behavior happen
8613
8719
  // (after which we retroactively handle them and reset the DOM) to
8614
8720
  // avoid messing up the virtual keyboard state.
@@ -9038,6 +9144,9 @@
9038
9144
  view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
9039
9145
  view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
9040
9146
  };
9147
+ observers.wheel = observers.mousewheel = view => {
9148
+ view.inputState.lastWheelEvent = Date.now();
9149
+ };
9041
9150
  handlers.keydown = (view, event) => {
9042
9151
  view.inputState.setSelectionOrigin("select");
9043
9152
  if (event.keyCode == 27 && view.inputState.tabFocusMode != 0)
@@ -9045,8 +9154,13 @@
9045
9154
  return false;
9046
9155
  };
9047
9156
  observers.touchstart = (view, e) => {
9048
- view.inputState.lastTouchTime = Date.now();
9049
- view.inputState.setSelectionOrigin("select.pointer");
9157
+ let iState = view.inputState, touch = e.targetTouches[0];
9158
+ iState.lastTouchTime = Date.now();
9159
+ if (touch) {
9160
+ iState.lastTouchX = touch.clientX;
9161
+ iState.lastTouchY = touch.clientY;
9162
+ }
9163
+ iState.setSelectionOrigin("select.pointer");
9050
9164
  };
9051
9165
  observers.touchmove = view => {
9052
9166
  view.inputState.setSelectionOrigin("select.pointer");
@@ -9282,8 +9396,7 @@
9282
9396
  // spans multiple elements including this CodeMirror. The copy event may
9283
9397
  // bubble through CodeMirror (e.g. when CodeMirror is the first or the last
9284
9398
  // 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))
9399
+ if (!hasSelection(view.contentDOM, view.observer.selectionRange))
9287
9400
  return false;
9288
9401
  let { text, ranges, linewise } = copiedRange(view.state);
9289
9402
  if (!text && !linewise)
@@ -10282,7 +10395,8 @@
10282
10395
  get estimatedHeight() { return this.vertical ? this.size : -1; }
10283
10396
  }
10284
10397
  class ViewState {
10285
- constructor(state) {
10398
+ constructor(view, state) {
10399
+ this.view = view;
10286
10400
  this.state = state;
10287
10401
  // These are contentDOM-local coordinates
10288
10402
  this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
@@ -10293,12 +10407,14 @@
10293
10407
  this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height
10294
10408
  this.editorHeight = 0; // scrollDOM.clientHeight, unscaled
10295
10409
  this.editorWidth = 0; // scrollDOM.clientWidth, unscaled
10296
- this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled
10297
- this.scrolledToBottom = false;
10298
10410
  // The CSS-transformation scale of the editor (transformed size /
10299
10411
  // concrete size)
10300
10412
  this.scaleX = 1;
10301
10413
  this.scaleY = 1;
10414
+ // Last seen vertical offset of the element at the top of the scroll
10415
+ // container, or top of the window if there's no wrapping scroller
10416
+ this.scrollOffset = 0;
10417
+ this.scrolledToBottom = false;
10302
10418
  // The vertical position (document-relative) to which to anchor the
10303
10419
  // scroll position. -1 means anchor to the end of the document.
10304
10420
  this.scrollAnchorPos = 0;
@@ -10336,6 +10452,7 @@
10336
10452
  this.updateViewportLines();
10337
10453
  this.lineGaps = this.ensureLineGaps([]);
10338
10454
  this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
10455
+ this.scrollParent = view.scrollDOM;
10339
10456
  this.computeVisibleRanges();
10340
10457
  }
10341
10458
  updateForViewport() {
@@ -10369,7 +10486,7 @@
10369
10486
  let contentChanges = update.changedRanges;
10370
10487
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
10371
10488
  let prevHeight = this.heightMap.height;
10372
- let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
10489
+ let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollOffset);
10373
10490
  clearHeightChangeFlag();
10374
10491
  this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
10375
10492
  if (this.heightMap.height != prevHeight || heightChangeFlag)
@@ -10401,12 +10518,12 @@
10401
10518
  !update.state.facet(nativeSelectionHidden))
10402
10519
  this.mustEnforceCursorAssoc = true;
10403
10520
  }
10404
- measure(view) {
10405
- let dom = view.contentDOM, style = window.getComputedStyle(dom);
10521
+ measure() {
10522
+ let { view } = this, dom = view.contentDOM, style = window.getComputedStyle(dom);
10406
10523
  let oracle = this.heightOracle;
10407
10524
  let whiteSpace = style.whiteSpace;
10408
10525
  this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
10409
- let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent;
10526
+ let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace) || this.mustMeasureContent === "refresh";
10410
10527
  let domRect = dom.getBoundingClientRect();
10411
10528
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height;
10412
10529
  this.contentDOMHeight = domRect.height;
@@ -10436,12 +10553,18 @@
10436
10553
  this.editorWidth = view.scrollDOM.clientWidth;
10437
10554
  result |= 16 /* UpdateFlag.Geometry */;
10438
10555
  }
10439
- let scrollTop = view.scrollDOM.scrollTop * this.scaleY;
10440
- if (this.scrollTop != scrollTop) {
10556
+ let scrollParent = scrollableParents(this.view.contentDOM, false).y;
10557
+ if (scrollParent != this.scrollParent) {
10558
+ this.scrollParent = scrollParent;
10441
10559
  this.scrollAnchorHeight = -1;
10442
- this.scrollTop = scrollTop;
10560
+ this.scrollOffset = 0;
10443
10561
  }
10444
- this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
10562
+ let scrollOffset = this.getScrollOffset();
10563
+ if (this.scrollOffset != scrollOffset) {
10564
+ this.scrollAnchorHeight = -1;
10565
+ this.scrollOffset = scrollOffset;
10566
+ }
10567
+ this.scrolledToBottom = isScrolledToBottom(this.scrollParent || view.win);
10445
10568
  // Pixel viewport
10446
10569
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
10447
10570
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -10718,9 +10841,14 @@
10718
10841
  this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
10719
10842
  scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType$1.ByHeight, this.heightOracle, 0, 0), this.scaler);
10720
10843
  }
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];
10844
+ getScrollOffset() {
10845
+ let base = this.scrollParent == this.view.scrollDOM ? this.scrollParent.scrollTop
10846
+ : (this.scrollParent ? this.scrollParent.getBoundingClientRect().top : 0) - this.view.contentDOM.getBoundingClientRect().top;
10847
+ return base * this.scaleY;
10848
+ }
10849
+ scrollAnchorAt(scrollOffset) {
10850
+ let block = this.lineBlockAtHeight(scrollOffset + 8);
10851
+ return block.from >= this.viewport.from || this.viewportLines[0].top - scrollOffset > 200 ? block : this.viewportLines[0];
10724
10852
  }
10725
10853
  elementAtHeight(height) {
10726
10854
  return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
@@ -10973,6 +11101,21 @@
10973
11101
  "&dark .cm-cursor": {
10974
11102
  borderLeftColor: "#ddd"
10975
11103
  },
11104
+ ".cm-selectionHandle": {
11105
+ backgroundColor: "currentColor",
11106
+ width: "1.5px"
11107
+ },
11108
+ ".cm-selectionHandle-start::before, .cm-selectionHandle-end::before": {
11109
+ content: '""',
11110
+ backgroundColor: "inherit",
11111
+ borderRadius: "50%",
11112
+ width: "8px",
11113
+ height: "8px",
11114
+ position: "absolute",
11115
+ left: "-3.25px"
11116
+ },
11117
+ ".cm-selectionHandle-start::before": { top: "-8px" },
11118
+ ".cm-selectionHandle-end::before": { bottom: "-8px" },
10976
11119
  ".cm-dropCursor": {
10977
11120
  position: "absolute"
10978
11121
  },
@@ -11967,7 +12110,7 @@
11967
12110
  ((trs) => this.update(trs));
11968
12111
  this.dispatch = this.dispatch.bind(this);
11969
12112
  this._root = (config.root || getRoot(config.parent) || document);
11970
- this.viewState = new ViewState(config.state || EditorState.create(config));
12113
+ this.viewState = new ViewState(this, config.state || EditorState.create(config));
11971
12114
  if (config.scrollTo && config.scrollTo.is(scrollIntoView$1))
11972
12115
  this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state);
11973
12116
  this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
@@ -11983,7 +12126,7 @@
11983
12126
  this.requestMeasure();
11984
12127
  if ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready)
11985
12128
  document.fonts.ready.then(() => {
11986
- this.viewState.mustMeasureContent = true;
12129
+ this.viewState.mustMeasureContent = "refresh";
11987
12130
  this.requestMeasure();
11988
12131
  });
11989
12132
  }
@@ -12122,7 +12265,7 @@
12122
12265
  try {
12123
12266
  for (let plugin of this.plugins)
12124
12267
  plugin.destroy(this);
12125
- this.viewState = new ViewState(newState);
12268
+ this.viewState = new ViewState(this, newState);
12126
12269
  this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
12127
12270
  this.pluginMap.clear();
12128
12271
  for (let plugin of this.plugins)
@@ -12201,26 +12344,26 @@
12201
12344
  if (flush)
12202
12345
  this.observer.forceFlush();
12203
12346
  let updated = null;
12204
- let sDOM = this.scrollDOM, scrollTop = sDOM.scrollTop * this.scaleY;
12347
+ let scroll = this.viewState.scrollParent, scrollOffset = this.viewState.getScrollOffset();
12205
12348
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
12206
- if (Math.abs(scrollTop - this.viewState.scrollTop) > 1)
12349
+ if (Math.abs(scrollOffset - this.viewState.scrollOffset) > 1)
12207
12350
  scrollAnchorHeight = -1;
12208
12351
  this.viewState.scrollAnchorHeight = -1;
12209
12352
  try {
12210
12353
  for (let i = 0;; i++) {
12211
12354
  if (scrollAnchorHeight < 0) {
12212
- if (isScrolledToBottom(sDOM)) {
12355
+ if (isScrolledToBottom(scroll || this.win)) {
12213
12356
  scrollAnchorPos = -1;
12214
12357
  scrollAnchorHeight = this.viewState.heightMap.height;
12215
12358
  }
12216
12359
  else {
12217
- let block = this.viewState.scrollAnchorAt(scrollTop);
12360
+ let block = this.viewState.scrollAnchorAt(scrollOffset);
12218
12361
  scrollAnchorPos = block.from;
12219
12362
  scrollAnchorHeight = block.top;
12220
12363
  }
12221
12364
  }
12222
12365
  this.updateState = 1 /* UpdateState.Measuring */;
12223
- let changed = this.viewState.measure(this);
12366
+ let changed = this.viewState.measure();
12224
12367
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
12225
12368
  break;
12226
12369
  if (i > 5) {
@@ -12281,10 +12424,15 @@
12281
12424
  else {
12282
12425
  let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
12283
12426
  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;
12427
+ let diff = (newAnchorHeight - scrollAnchorHeight) / this.scaleY;
12428
+ if ((diff > 1 || diff < -1) &&
12429
+ (scroll == this.scrollDOM || this.hasFocus ||
12430
+ Math.max(this.inputState.lastWheelEvent, this.inputState.lastTouchTime) > Date.now() - 100)) {
12431
+ scrollOffset = scrollOffset + diff;
12432
+ if (scroll)
12433
+ scroll.scrollTop += diff;
12434
+ else
12435
+ this.win.scrollBy(0, diff);
12288
12436
  scrollAnchorHeight = -1;
12289
12437
  continue;
12290
12438
  }
@@ -13532,7 +13680,8 @@
13532
13680
  combine(configs) {
13533
13681
  return combineConfig(configs, {
13534
13682
  cursorBlinkRate: 1200,
13535
- drawRangeCursor: true
13683
+ drawRangeCursor: true,
13684
+ iosSelectionHandles: true
13536
13685
  }, {
13537
13686
  cursorBlinkRate: (a, b) => Math.min(a, b),
13538
13687
  drawRangeCursor: (a, b) => a || b
@@ -13576,7 +13725,7 @@
13576
13725
  let cursors = [];
13577
13726
  for (let r of state.selection.ranges) {
13578
13727
  let prim = r == state.selection.main;
13579
- if (r.empty || conf.drawRangeCursor) {
13728
+ if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
13580
13729
  let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
13581
13730
  let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1);
13582
13731
  for (let piece of RectangleMarker.forRange(view, className, cursor))
@@ -13604,8 +13753,19 @@
13604
13753
  const selectionLayer = /*@__PURE__*/layer({
13605
13754
  above: false,
13606
13755
  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));
13756
+ let markers = [], { main, ranges } = view.state.selection;
13757
+ for (let r of ranges)
13758
+ if (!r.empty) {
13759
+ for (let marker of RectangleMarker.forRange(view, "cm-selectionBackground", r))
13760
+ markers.push(marker);
13761
+ }
13762
+ if (browser.ios && !main.empty && view.state.facet(selectionConfig).iosSelectionHandles) {
13763
+ for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-start", EditorSelection.cursor(main.from, 1)))
13764
+ markers.push(piece);
13765
+ for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-end", EditorSelection.cursor(main.to, 1)))
13766
+ markers.push(piece);
13767
+ }
13768
+ return markers;
13609
13769
  },
13610
13770
  update(update, dom) {
13611
13771
  return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
@@ -19937,30 +20097,44 @@
19937
20097
  decorations.push(mark.range(match.end.from, match.end.to));
19938
20098
  return decorations;
19939
20099
  }
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)
20100
+ function bracketDeco(state) {
20101
+ let decorations = [];
20102
+ let config = state.facet(bracketMatchingConfig);
20103
+ for (let range of state.selection.ranges) {
20104
+ if (!range.empty)
20105
+ continue;
20106
+ let match = matchBrackets(state, range.head, -1, config)
20107
+ || (range.head > 0 && matchBrackets(state, range.head - 1, 1, config))
20108
+ || (config.afterCursor &&
20109
+ (matchBrackets(state, range.head, 1, config) ||
20110
+ (range.head < state.doc.length && matchBrackets(state, range.head + 1, -1, config))));
20111
+ if (match)
20112
+ decorations = decorations.concat(config.renderMatch(match, state));
20113
+ }
20114
+ return Decoration.set(decorations, true);
20115
+ }
20116
+ const bracketMatcher = /*@__PURE__*/ViewPlugin.fromClass(class {
20117
+ constructor(view) {
20118
+ this.paused = false;
20119
+ this.decorations = bracketDeco(view.state);
20120
+ }
20121
+ update(update) {
20122
+ if (update.docChanged || update.selectionSet || this.paused) {
20123
+ if (update.view.composing) {
20124
+ this.decorations = this.decorations.map(update.changes);
20125
+ this.paused = true;
20126
+ }
20127
+ else {
20128
+ this.decorations = bracketDeco(update.state);
20129
+ this.paused = false;
20130
+ }
20131
+ }
20132
+ }
20133
+ }, {
20134
+ decorations: v => v.decorations
19961
20135
  });
19962
20136
  const bracketMatchingUnique = [
19963
- bracketMatchingState,
20137
+ bracketMatcher,
19964
20138
  baseTheme$3
19965
20139
  ];
19966
20140
  /**
@@ -23231,6 +23405,7 @@
23231
23405
  };
23232
23406
  }
23233
23407
 
23408
+ const setSelectedEffect = /*@__PURE__*/StateEffect.define();
23234
23409
  function optionContent(config) {
23235
23410
  let content = config.addToOptions.slice();
23236
23411
  if (config.icons)
@@ -23322,6 +23497,16 @@
23322
23497
  return;
23323
23498
  }
23324
23499
  }
23500
+ if (e.target == this.list) {
23501
+ let move = this.list.classList.contains("cm-completionListIncompleteTop") &&
23502
+ e.clientY < this.list.firstChild.getBoundingClientRect().top ? this.range.from - 1 :
23503
+ this.list.classList.contains("cm-completionListIncompleteBottom") &&
23504
+ e.clientY > this.list.lastChild.getBoundingClientRect().bottom ? this.range.to : null;
23505
+ if (move != null) {
23506
+ view.dispatch({ effects: setSelectedEffect.of(move) });
23507
+ e.preventDefault();
23508
+ }
23509
+ }
23325
23510
  });
23326
23511
  this.dom.addEventListener("focusout", (e) => {
23327
23512
  let state = view.state.field(this.stateField, false);
@@ -23823,7 +24008,6 @@
23823
24008
  const setActiveEffect = /*@__PURE__*/StateEffect.define({
23824
24009
  map(sources, mapping) { return sources.map(s => s.map(mapping)); }
23825
24010
  });
23826
- const setSelectedEffect = /*@__PURE__*/StateEffect.define();
23827
24011
  const completionState = /*@__PURE__*/StateField.define({
23828
24012
  create() { return CompletionState.start(); },
23829
24013
  update(value, tr) { return value.update(tr); },
@@ -24978,10 +25162,10 @@
24978
25162
  let field = view.state.field(lintState, false);
24979
25163
  if (!field)
24980
25164
  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)
25165
+ let sel = view.state.selection.main, next = findDiagnostic(field.diagnostics, null, sel.to + 1);
25166
+ if (!next) {
25167
+ next = findDiagnostic(field.diagnostics, null, 0);
25168
+ if (!next || next.from == sel.from && next.to == sel.to)
24985
25169
  return false;
24986
25170
  }
24987
25171
  view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
@@ -25353,6 +25537,12 @@
25353
25537
  padding: 0,
25354
25538
  margin: 0
25355
25539
  }
25540
+ },
25541
+ "&dark .cm-lintRange-active": { backgroundColor: "#86714a80" },
25542
+ "&dark .cm-panel.cm-panel-lint ul": {
25543
+ "& [aria-selected]": {
25544
+ backgroundColor: "#2e343e",
25545
+ },
25356
25546
  }
25357
25547
  });
25358
25548
  function severityWeight(sev) {
@@ -27725,7 +27915,21 @@
27725
27915
  }),
27726
27916
  /*@__PURE__*/foldNodeProp.add({
27727
27917
  "Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType": foldInside,
27728
- BlockComment(tree) { return { from: tree.from + 2, to: tree.to - 2 }; }
27918
+ BlockComment(tree) { return { from: tree.from + 2, to: tree.to - 2 }; },
27919
+ JSXElement(tree) {
27920
+ let open = tree.firstChild;
27921
+ if (!open || open.name == "JSXSelfClosingTag")
27922
+ return null;
27923
+ let close = tree.lastChild;
27924
+ return { from: open.to, to: close.type.isError ? tree.to : close.from };
27925
+ },
27926
+ "JSXSelfClosingTag JSXOpenTag"(tree) {
27927
+ var _a;
27928
+ let name = (_a = tree.firstChild) === null || _a === void 0 ? void 0 : _a.nextSibling, close = tree.lastChild;
27929
+ if (!name || name.type.isError)
27930
+ return null;
27931
+ return { from: name.to, to: close.type.isError ? tree.to : close.from };
27932
+ }
27729
27933
  })
27730
27934
  ]
27731
27935
  }),