@batchfy/codemirror-minimap 0.1.0 → 0.1.1
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.
- package/README.md +5 -0
- package/dist/Overlay.d.ts.map +1 -1
- package/dist/geometry.d.ts +130 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +321 -166
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/Overlay.ts +151 -118
- package/src/geometry.ts +402 -0
- package/src/index.ts +39 -25
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Facet, combineConfig, StateField } from '@codemirror/state';
|
|
2
2
|
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
3
|
+
import { foldEffect, unfoldEffect, foldedRanges, language, highlightingFor } from '@codemirror/language';
|
|
3
4
|
import crelt from 'crelt';
|
|
4
5
|
import { setDiagnosticsEffect, diagnosticCount, forEachDiagnostic } from '@codemirror/lint';
|
|
5
|
-
import { foldEffect, unfoldEffect, foldedRanges, language, highlightingFor } from '@codemirror/language';
|
|
6
6
|
import { highlightTree } from '@lezer/highlight';
|
|
7
7
|
import { TreeFragment } from '@lezer/common';
|
|
8
8
|
|
|
@@ -41,12 +41,224 @@ const Config = Facet.define({
|
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
function computeLinesState(state) {
|
|
45
|
+
if (!state.facet(Config).enabled) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const lines = [];
|
|
49
|
+
const lineCursor = state.doc.iterLines();
|
|
50
|
+
const foldedRangeCursor = foldedRanges(state).iter();
|
|
51
|
+
let textOffset = 0;
|
|
52
|
+
lineCursor.next();
|
|
53
|
+
while (!lineCursor.done) {
|
|
54
|
+
const lineText = lineCursor.value;
|
|
55
|
+
const from = textOffset;
|
|
56
|
+
const to = from + lineText.length;
|
|
57
|
+
while (foldedRangeCursor.value && foldedRangeCursor.to < from) {
|
|
58
|
+
foldedRangeCursor.next();
|
|
59
|
+
}
|
|
60
|
+
const { from: foldFrom, to: foldTo } = foldedRangeCursor;
|
|
61
|
+
const lineStartInFold = from >= foldFrom && from < foldTo;
|
|
62
|
+
const lineEndsInFold = to > foldFrom && to <= foldTo;
|
|
63
|
+
if (lineStartInFold) {
|
|
64
|
+
const lastLine = lines.pop() ?? [];
|
|
65
|
+
const lastRange = lastLine.pop();
|
|
66
|
+
if (lastRange && lastRange.folded) {
|
|
67
|
+
lastRange.to = foldTo;
|
|
68
|
+
}
|
|
69
|
+
if (lastRange) {
|
|
70
|
+
lastLine.push(lastRange);
|
|
71
|
+
}
|
|
72
|
+
if (!lastRange || !lastRange.folded) {
|
|
73
|
+
lastLine.push({ from: foldFrom, to: foldTo, folded: true });
|
|
74
|
+
}
|
|
75
|
+
if (!lineEndsInFold) {
|
|
76
|
+
lastLine.push({ from: foldTo, to, folded: false });
|
|
77
|
+
}
|
|
78
|
+
lines.push(lastLine);
|
|
79
|
+
} else if (lineEndsInFold) {
|
|
80
|
+
lines.push([
|
|
81
|
+
{ from, to: foldFrom, folded: false },
|
|
82
|
+
{ from: foldFrom, to: foldTo, folded: true }
|
|
83
|
+
]);
|
|
84
|
+
} else {
|
|
85
|
+
lines.push([{ from, to, folded: false }]);
|
|
86
|
+
}
|
|
87
|
+
textOffset = to + 1;
|
|
88
|
+
lineCursor.next();
|
|
89
|
+
}
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
92
|
+
const LinesState = StateField.define({
|
|
93
|
+
create: (state) => computeLinesState(state),
|
|
94
|
+
update: (current, tr) => {
|
|
95
|
+
if (foldsChanged([tr]) || tr.docChanged) {
|
|
96
|
+
return computeLinesState(tr.state);
|
|
97
|
+
}
|
|
98
|
+
return current;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
function foldsChanged(transactions) {
|
|
102
|
+
return transactions.some(
|
|
103
|
+
(tr) => tr.effects.some((ef) => ef.is(foldEffect) || ef.is(unfoldEffect))
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const SCALE = Scale.PixelMultiplier * Scale.SizeRatio;
|
|
108
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
109
|
+
function computeLayout(input) {
|
|
110
|
+
const { rowCount, lineHeight, paddingTop, paddingBottom, height } = input;
|
|
111
|
+
const lastRow = Math.max(0, rowCount - 1);
|
|
112
|
+
const first = clamp(input.firstVisibleRow, 0, lastRow);
|
|
113
|
+
const last = clamp(input.lastVisibleRow, first, lastRow);
|
|
114
|
+
const visibleRows2 = last - first + 1;
|
|
115
|
+
const contentHeight = paddingTop + paddingBottom + rowCount * lineHeight;
|
|
116
|
+
const overlayHeight = Math.min(height, visibleRows2 * lineHeight);
|
|
117
|
+
const rowTop = paddingTop + first * lineHeight;
|
|
118
|
+
const scrollableRows = Math.max(0, rowCount - input.rowsPerScreen);
|
|
119
|
+
const progress = scrollableRows <= 0 ? 0 : clamp(first / scrollableRows, 0, 1);
|
|
120
|
+
const maxScroll = Math.max(0, contentHeight - height);
|
|
121
|
+
const scrollTop = clamp(
|
|
122
|
+
maxScroll * progress,
|
|
123
|
+
Math.max(0, rowTop - (height - overlayHeight)),
|
|
124
|
+
Math.min(maxScroll, rowTop)
|
|
125
|
+
);
|
|
126
|
+
const overlayTravel = Math.max(
|
|
127
|
+
0,
|
|
128
|
+
Math.min(height, contentHeight) - paddingTop - paddingBottom - input.rowsPerScreen * lineHeight
|
|
129
|
+
);
|
|
130
|
+
return {
|
|
131
|
+
contentHeight,
|
|
132
|
+
scrollTop,
|
|
133
|
+
overlayTop: rowTop - scrollTop,
|
|
134
|
+
overlayHeight,
|
|
135
|
+
rowCount,
|
|
136
|
+
scrollableRows,
|
|
137
|
+
overlayTravel
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const rowHeights = /* @__PURE__ */ new WeakMap();
|
|
141
|
+
function setRowHeight(view, canvasLineHeight) {
|
|
142
|
+
const lineHeight = canvasLineHeight / Scale.PixelMultiplier;
|
|
143
|
+
if (rowHeights.get(view) !== lineHeight) {
|
|
144
|
+
rowHeights.set(view, lineHeight);
|
|
145
|
+
measured.delete(view);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function rowAtPos(lines, pos) {
|
|
149
|
+
let low = 0;
|
|
150
|
+
let high = lines.length - 1;
|
|
151
|
+
while (low < high) {
|
|
152
|
+
const mid = Math.ceil((low + high) / 2);
|
|
153
|
+
const line = lines[mid];
|
|
154
|
+
if (line && line.length && line[0].from <= pos) {
|
|
155
|
+
low = mid;
|
|
156
|
+
} else {
|
|
157
|
+
high = mid - 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return Math.max(0, low);
|
|
161
|
+
}
|
|
162
|
+
function lineBlockAtHeight(view, height) {
|
|
163
|
+
const { doc } = view.state;
|
|
164
|
+
let low = 1;
|
|
165
|
+
let high = doc.lines;
|
|
166
|
+
while (low < high) {
|
|
167
|
+
const mid = Math.floor((low + high) / 2);
|
|
168
|
+
if (view.lineBlockAt(doc.line(mid).from).bottom <= height) {
|
|
169
|
+
low = mid + 1;
|
|
170
|
+
} else {
|
|
171
|
+
high = mid;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return view.lineBlockAt(doc.line(low).from);
|
|
175
|
+
}
|
|
176
|
+
function visibleRows(view) {
|
|
177
|
+
const lines = view.state.field(LinesState);
|
|
178
|
+
const { top, bottom } = view.scrollDOM.getBoundingClientRect();
|
|
179
|
+
const documentTop = view.documentTop;
|
|
180
|
+
return {
|
|
181
|
+
first: rowAtPos(lines, lineBlockAtHeight(view, top - documentTop).from),
|
|
182
|
+
last: rowAtPos(lines, lineBlockAtHeight(view, bottom - documentTop).to)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const measured = /* @__PURE__ */ new WeakMap();
|
|
186
|
+
function readGeometry(view, token) {
|
|
187
|
+
if (token) {
|
|
188
|
+
const previous = measured.get(view);
|
|
189
|
+
if (previous && previous.token === token) {
|
|
190
|
+
return previous.geometry;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const geometry = measureGeometry(view);
|
|
194
|
+
if (token) {
|
|
195
|
+
measured.set(view, { token, geometry });
|
|
196
|
+
}
|
|
197
|
+
return geometry;
|
|
198
|
+
}
|
|
199
|
+
function measureGeometry(view) {
|
|
200
|
+
const lineHeight = rowHeights.get(view) || view.defaultLineHeight / SCALE || 0;
|
|
201
|
+
const paddingTop = view.documentPadding.top / SCALE;
|
|
202
|
+
const paddingBottom = view.documentPadding.bottom / SCALE;
|
|
203
|
+
const height = view.dom.getBoundingClientRect().height;
|
|
204
|
+
const { first, last } = visibleRows(view);
|
|
205
|
+
const layout = computeLayout({
|
|
206
|
+
rowCount: view.state.field(LinesState).length,
|
|
207
|
+
lineHeight,
|
|
208
|
+
paddingTop,
|
|
209
|
+
paddingBottom,
|
|
210
|
+
height,
|
|
211
|
+
firstVisibleRow: first,
|
|
212
|
+
lastVisibleRow: last,
|
|
213
|
+
rowsPerScreen: view.defaultLineHeight ? view.scrollDOM.clientHeight / view.defaultLineHeight : 0
|
|
214
|
+
});
|
|
215
|
+
return { ...layout, lineHeight, paddingTop, paddingBottom, height };
|
|
216
|
+
}
|
|
217
|
+
function rowAtY(geometry, y) {
|
|
218
|
+
if (geometry.lineHeight <= 0) {
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
const offset = geometry.scrollTop + y - geometry.paddingTop;
|
|
222
|
+
return Math.floor(offset / geometry.lineHeight);
|
|
223
|
+
}
|
|
224
|
+
function rowAtOverlayTop(geometry, overlayTop) {
|
|
225
|
+
if (geometry.overlayTravel <= 0 || geometry.scrollableRows <= 0 || geometry.lineHeight <= 0) {
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
const offset = overlayTop - geometry.paddingTop;
|
|
229
|
+
if (offset >= geometry.overlayTravel) {
|
|
230
|
+
return clamp(
|
|
231
|
+
geometry.scrollableRows + (offset - geometry.overlayTravel) / geometry.lineHeight,
|
|
232
|
+
0,
|
|
233
|
+
Math.max(0, geometry.rowCount - 1)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const progress = clamp(offset / geometry.overlayTravel, 0, 1);
|
|
237
|
+
return progress * geometry.scrollableRows;
|
|
238
|
+
}
|
|
239
|
+
function scrollToRow(view, row, offset = 0) {
|
|
240
|
+
const lines = view.state.field(LinesState);
|
|
241
|
+
const wanted = clamp(row, 0, lines.length - 1);
|
|
242
|
+
const index = Math.floor(wanted);
|
|
243
|
+
const line = lines[index];
|
|
244
|
+
if (!line || !line.length) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const block = view.lineBlockAt(line[0].from);
|
|
248
|
+
const withinRow = (wanted - index) * block.height;
|
|
249
|
+
view.scrollDOM.scrollTop = view.documentPadding.top + block.top + withinRow - offset;
|
|
250
|
+
}
|
|
251
|
+
|
|
44
252
|
const Theme$1 = EditorView.theme({
|
|
45
253
|
".cm-minimap-overlay-container": {
|
|
46
254
|
position: "absolute",
|
|
47
255
|
top: 0,
|
|
48
256
|
height: "100%",
|
|
49
257
|
width: "100%",
|
|
258
|
+
// The minimap is a sibling of `.cm-content` inside the scroller, so
|
|
259
|
+
// without this it inherits the caret the editor shows over text. It is
|
|
260
|
+
// not text: clicking it jumps, and the overlay is dragged.
|
|
261
|
+
cursor: "default",
|
|
50
262
|
"&.cm-minimap-overlay-mouse-over": {
|
|
51
263
|
opacity: 0,
|
|
52
264
|
transition: "visibility 0s linear 300ms, opacity 300ms"
|
|
@@ -66,6 +278,7 @@ const Theme$1 = EditorView.theme({
|
|
|
66
278
|
top: 0,
|
|
67
279
|
width: "100%",
|
|
68
280
|
transition: "top 0s ease-in 0ms",
|
|
281
|
+
cursor: "grab",
|
|
69
282
|
"&:hover": {
|
|
70
283
|
opacity: "0.3"
|
|
71
284
|
}
|
|
@@ -75,12 +288,12 @@ const Theme$1 = EditorView.theme({
|
|
|
75
288
|
visibility: "visible",
|
|
76
289
|
transition: "visibility 0s linear 0ms, opacity 300ms",
|
|
77
290
|
"& .cm-minimap-overlay": {
|
|
78
|
-
opacity: "0.4"
|
|
291
|
+
opacity: "0.4",
|
|
292
|
+
cursor: "grabbing"
|
|
79
293
|
}
|
|
80
294
|
}
|
|
81
295
|
}
|
|
82
296
|
});
|
|
83
|
-
const SCALE = Scale.PixelMultiplier * Scale.SizeRatio;
|
|
84
297
|
const OverlayView = ViewPlugin.fromClass(
|
|
85
298
|
class {
|
|
86
299
|
constructor(view) {
|
|
@@ -93,7 +306,29 @@ const OverlayView = ViewPlugin.fromClass(
|
|
|
93
306
|
container;
|
|
94
307
|
dom;
|
|
95
308
|
_isDragging = false;
|
|
96
|
-
|
|
309
|
+
/**
|
|
310
|
+
* Where inside the overlay the drag took hold of it, as a fraction of
|
|
311
|
+
* its height rather than a count of pixels down from its top.
|
|
312
|
+
*
|
|
313
|
+
* The overlay is sized to the lines on screen, so it shrinks as a drag
|
|
314
|
+
* carries it from a screen of code into one of wrapped prose. A pixel
|
|
315
|
+
* offset taken when it was tall goes on pointing at where its middle
|
|
316
|
+
* used to be, which by the end of the document is somewhere below its
|
|
317
|
+
* bottom edge — and the pointer would have to leave the minimap, often
|
|
318
|
+
* the window, to put the overlay where it belongs. A fraction shrinks
|
|
319
|
+
* with it and stays under the thumb.
|
|
320
|
+
*/
|
|
321
|
+
_dragAnchor;
|
|
322
|
+
/** The last position the pointer reported, waiting for a frame. */
|
|
323
|
+
_dragPointerY;
|
|
324
|
+
_dragFrame;
|
|
325
|
+
/**
|
|
326
|
+
* Measured when the drag starts and kept for its duration. None of it
|
|
327
|
+
* moves as the document scrolls, and `update` drops it so that anything
|
|
328
|
+
* which does move it — an edit, a resize, a change of theme — is
|
|
329
|
+
* measured again.
|
|
330
|
+
*/
|
|
331
|
+
_dragGeometry;
|
|
97
332
|
// Bound once so that removeEventListener sees the same reference that
|
|
98
333
|
// addEventListener was given; `.bind` at call time yields a new
|
|
99
334
|
// function each call and the listener would never be detached.
|
|
@@ -114,10 +349,12 @@ const OverlayView = ViewPlugin.fromClass(
|
|
|
114
349
|
inner.appendChild(this.container);
|
|
115
350
|
}
|
|
116
351
|
this.computeShowOverlay();
|
|
117
|
-
this.
|
|
118
|
-
this.computeTop();
|
|
352
|
+
this.position();
|
|
119
353
|
}
|
|
120
354
|
remove() {
|
|
355
|
+
if (this._isDragging) {
|
|
356
|
+
this.endDrag();
|
|
357
|
+
}
|
|
121
358
|
if (this.container) {
|
|
122
359
|
this.container.removeEventListener(
|
|
123
360
|
"mousedown",
|
|
@@ -139,33 +376,26 @@ const OverlayView = ViewPlugin.fromClass(
|
|
|
139
376
|
this.create(update.view);
|
|
140
377
|
}
|
|
141
378
|
if (now) {
|
|
379
|
+
this._dragGeometry = void 0;
|
|
142
380
|
this.computeShowOverlay();
|
|
143
|
-
|
|
144
|
-
this.computeHeight();
|
|
145
|
-
this.computeTop();
|
|
146
|
-
}
|
|
381
|
+
this.position(update);
|
|
147
382
|
}
|
|
148
383
|
}
|
|
149
|
-
|
|
384
|
+
/**
|
|
385
|
+
* Covers the rows the editor is showing. The renderer scrolls its rows
|
|
386
|
+
* to the position the same geometry prescribes, so the overlay lands on
|
|
387
|
+
* the drawn content whatever the document does with wrapping or folds.
|
|
388
|
+
*/
|
|
389
|
+
position(token) {
|
|
150
390
|
if (!this.dom) {
|
|
151
391
|
return;
|
|
152
392
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const maxScrollTop = scrollHeight - clientHeight;
|
|
160
|
-
const topForNonOverflowing = scrollTop / SCALE;
|
|
161
|
-
const height = clientHeight / SCALE;
|
|
162
|
-
const maxTop = clientHeight - height;
|
|
163
|
-
let scrollRatio = scrollTop / maxScrollTop;
|
|
164
|
-
if (isNaN(scrollRatio)) scrollRatio = 0;
|
|
165
|
-
const topForOverflowing = maxTop * scrollRatio;
|
|
166
|
-
const top = Math.min(topForOverflowing, topForNonOverflowing);
|
|
167
|
-
this.dom.style.top = top + "px";
|
|
168
|
-
}
|
|
393
|
+
const { overlayTop, overlayHeight } = readGeometry(
|
|
394
|
+
this.view,
|
|
395
|
+
token
|
|
396
|
+
);
|
|
397
|
+
this.dom.style.top = overlayTop + "px";
|
|
398
|
+
this.dom.style.height = overlayHeight + "px";
|
|
169
399
|
}
|
|
170
400
|
computeShowOverlay() {
|
|
171
401
|
if (!this.container) {
|
|
@@ -193,75 +423,64 @@ const OverlayView = ViewPlugin.fromClass(
|
|
|
193
423
|
if (event.button === 2) {
|
|
194
424
|
return;
|
|
195
425
|
}
|
|
426
|
+
event.preventDefault();
|
|
196
427
|
const { clientY, target } = event;
|
|
197
|
-
if (target === this.dom) {
|
|
198
|
-
|
|
428
|
+
if (target === this.dom && this.dom) {
|
|
429
|
+
const grabbed = this.dom.getBoundingClientRect();
|
|
430
|
+
this._dragAnchor = grabbed.height > 0 ? (clientY - grabbed.top) / grabbed.height : 0;
|
|
199
431
|
this._isDragging = true;
|
|
200
432
|
this.container.classList.add("cm-minimap-overlay-active");
|
|
433
|
+
document.body.style.cursor = "grabbing";
|
|
201
434
|
return;
|
|
202
435
|
}
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.view.scrollDOM.scrollTop = top + deltaY - clientHeight / 2;
|
|
436
|
+
const geometry = readGeometry(this.view);
|
|
437
|
+
const y = clientY - this.container.getBoundingClientRect().top;
|
|
438
|
+
scrollToRow(
|
|
439
|
+
this.view,
|
|
440
|
+
rowAtY(geometry, y),
|
|
441
|
+
this.view.scrollDOM.clientHeight / 2
|
|
442
|
+
);
|
|
211
443
|
}
|
|
212
444
|
onMouseUp(_event) {
|
|
213
|
-
if (this._isDragging
|
|
214
|
-
this.
|
|
215
|
-
this._isDragging = false;
|
|
216
|
-
this.container.classList.remove("cm-minimap-overlay-active");
|
|
445
|
+
if (this._isDragging) {
|
|
446
|
+
this.endDrag();
|
|
217
447
|
}
|
|
218
448
|
}
|
|
449
|
+
endDrag() {
|
|
450
|
+
if (this._dragFrame !== void 0) {
|
|
451
|
+
cancelAnimationFrame(this._dragFrame);
|
|
452
|
+
this._dragFrame = void 0;
|
|
453
|
+
}
|
|
454
|
+
this._dragAnchor = void 0;
|
|
455
|
+
this._dragPointerY = void 0;
|
|
456
|
+
this._dragGeometry = void 0;
|
|
457
|
+
this._isDragging = false;
|
|
458
|
+
this.container?.classList.remove("cm-minimap-overlay-active");
|
|
459
|
+
document.body.style.removeProperty("cursor");
|
|
460
|
+
}
|
|
219
461
|
onMouseMove(event) {
|
|
220
|
-
if (!this._isDragging ||
|
|
462
|
+
if (!this._isDragging || this._dragAnchor === void 0) {
|
|
221
463
|
return;
|
|
222
464
|
}
|
|
223
465
|
event.preventDefault();
|
|
224
466
|
event.stopPropagation();
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const movingDown = deltaY > 0;
|
|
232
|
-
this._dragStartY = event.clientY;
|
|
233
|
-
const canvasHeight = this.dom.getBoundingClientRect().height;
|
|
234
|
-
const canvasAbsTop = this.dom.getBoundingClientRect().y;
|
|
235
|
-
const canvasAbsBot = canvasAbsTop + canvasHeight;
|
|
236
|
-
const canvasRelTopDouble = parseFloat(this.dom.style.top);
|
|
237
|
-
const scrollPosition = this.view.scrollDOM.scrollTop;
|
|
238
|
-
const editorHeight = this.view.scrollDOM.clientHeight;
|
|
239
|
-
const contentHeight = this.view.scrollDOM.scrollHeight;
|
|
240
|
-
const atTop = scrollPosition === 0;
|
|
241
|
-
const atBottom = Math.round(scrollPosition) >= Math.round(contentHeight - editorHeight);
|
|
242
|
-
if (atTop && movingUp || atTop && event.clientY < canvasAbsTop) {
|
|
243
|
-
return;
|
|
467
|
+
this._dragPointerY = event.clientY;
|
|
468
|
+
if (this._dragFrame === void 0) {
|
|
469
|
+
this._dragFrame = requestAnimationFrame(() => {
|
|
470
|
+
this._dragFrame = void 0;
|
|
471
|
+
this.dragToPointer();
|
|
472
|
+
});
|
|
244
473
|
}
|
|
245
|
-
|
|
474
|
+
}
|
|
475
|
+
dragToPointer() {
|
|
476
|
+
if (!this._isDragging || this._dragAnchor === void 0 || this._dragPointerY === void 0 || !this.container || !this.dom) {
|
|
246
477
|
return;
|
|
247
478
|
}
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
const relativeToMax = change / maxTopOverflowing;
|
|
254
|
-
const scrollPosOverflowing = (scrollHeight - clientHeight) * relativeToMax;
|
|
255
|
-
const scrollPosNonOverflowing = change * SCALE;
|
|
256
|
-
this.view.scrollDOM.scrollTop = Math.max(
|
|
257
|
-
scrollPosOverflowing,
|
|
258
|
-
scrollPosNonOverflowing
|
|
259
|
-
);
|
|
260
|
-
const top = Math.min(
|
|
261
|
-
Math.max(0, change),
|
|
262
|
-
Math.min(maxTopOverflowing, maxTopNonOverflowing)
|
|
263
|
-
);
|
|
264
|
-
this.dom.style.top = top + "px";
|
|
479
|
+
this._dragGeometry ??= readGeometry(this.view);
|
|
480
|
+
const overlayHeight = this.dom.getBoundingClientRect().height;
|
|
481
|
+
const containerTop = this.container.getBoundingClientRect().top;
|
|
482
|
+
const top = this._dragPointerY - this._dragAnchor * overlayHeight - containerTop;
|
|
483
|
+
scrollToRow(this.view, rowAtOverlayTop(this._dragGeometry, top));
|
|
265
484
|
}
|
|
266
485
|
destroy() {
|
|
267
486
|
this.remove();
|
|
@@ -269,8 +488,8 @@ const OverlayView = ViewPlugin.fromClass(
|
|
|
269
488
|
},
|
|
270
489
|
{
|
|
271
490
|
eventHandlers: {
|
|
272
|
-
scroll() {
|
|
273
|
-
requestAnimationFrame(() => this.
|
|
491
|
+
scroll(event) {
|
|
492
|
+
requestAnimationFrame(() => this.position(event));
|
|
274
493
|
}
|
|
275
494
|
}
|
|
276
495
|
}
|
|
@@ -292,69 +511,6 @@ class LineBasedState {
|
|
|
292
511
|
}
|
|
293
512
|
}
|
|
294
513
|
|
|
295
|
-
function computeLinesState(state) {
|
|
296
|
-
if (!state.facet(Config).enabled) {
|
|
297
|
-
return [];
|
|
298
|
-
}
|
|
299
|
-
const lines = [];
|
|
300
|
-
const lineCursor = state.doc.iterLines();
|
|
301
|
-
const foldedRangeCursor = foldedRanges(state).iter();
|
|
302
|
-
let textOffset = 0;
|
|
303
|
-
lineCursor.next();
|
|
304
|
-
while (!lineCursor.done) {
|
|
305
|
-
const lineText = lineCursor.value;
|
|
306
|
-
const from = textOffset;
|
|
307
|
-
const to = from + lineText.length;
|
|
308
|
-
while (foldedRangeCursor.value && foldedRangeCursor.to < from) {
|
|
309
|
-
foldedRangeCursor.next();
|
|
310
|
-
}
|
|
311
|
-
const { from: foldFrom, to: foldTo } = foldedRangeCursor;
|
|
312
|
-
const lineStartInFold = from >= foldFrom && from < foldTo;
|
|
313
|
-
const lineEndsInFold = to > foldFrom && to <= foldTo;
|
|
314
|
-
if (lineStartInFold) {
|
|
315
|
-
const lastLine = lines.pop() ?? [];
|
|
316
|
-
const lastRange = lastLine.pop();
|
|
317
|
-
if (lastRange && lastRange.folded) {
|
|
318
|
-
lastRange.to = foldTo;
|
|
319
|
-
}
|
|
320
|
-
if (lastRange) {
|
|
321
|
-
lastLine.push(lastRange);
|
|
322
|
-
}
|
|
323
|
-
if (!lastRange || !lastRange.folded) {
|
|
324
|
-
lastLine.push({ from: foldFrom, to: foldTo, folded: true });
|
|
325
|
-
}
|
|
326
|
-
if (!lineEndsInFold) {
|
|
327
|
-
lastLine.push({ from: foldTo, to, folded: false });
|
|
328
|
-
}
|
|
329
|
-
lines.push(lastLine);
|
|
330
|
-
} else if (lineEndsInFold) {
|
|
331
|
-
lines.push([
|
|
332
|
-
{ from, to: foldFrom, folded: false },
|
|
333
|
-
{ from: foldFrom, to: foldTo, folded: true }
|
|
334
|
-
]);
|
|
335
|
-
} else {
|
|
336
|
-
lines.push([{ from, to, folded: false }]);
|
|
337
|
-
}
|
|
338
|
-
textOffset = to + 1;
|
|
339
|
-
lineCursor.next();
|
|
340
|
-
}
|
|
341
|
-
return lines;
|
|
342
|
-
}
|
|
343
|
-
const LinesState = StateField.define({
|
|
344
|
-
create: (state) => computeLinesState(state),
|
|
345
|
-
update: (current, tr) => {
|
|
346
|
-
if (foldsChanged([tr]) || tr.docChanged) {
|
|
347
|
-
return computeLinesState(tr.state);
|
|
348
|
-
}
|
|
349
|
-
return current;
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
function foldsChanged(transactions) {
|
|
353
|
-
return transactions.some(
|
|
354
|
-
(tr) => tr.effects.some((ef) => ef.is(foldEffect) || ef.is(unfoldEffect))
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
514
|
class DiagnosticState extends LineBasedState {
|
|
359
515
|
count = void 0;
|
|
360
516
|
constructor(view) {
|
|
@@ -1009,7 +1165,7 @@ const minimapClass = ViewPlugin.fromClass(
|
|
|
1009
1165
|
this.text.update(update);
|
|
1010
1166
|
this.selection.update(update);
|
|
1011
1167
|
this.diagnostic.update(update);
|
|
1012
|
-
this.render();
|
|
1168
|
+
this.render(update);
|
|
1013
1169
|
}
|
|
1014
1170
|
}
|
|
1015
1171
|
getWidth() {
|
|
@@ -1020,7 +1176,12 @@ const minimapClass = ViewPlugin.fromClass(
|
|
|
1020
1176
|
}
|
|
1021
1177
|
return maxWidth;
|
|
1022
1178
|
}
|
|
1023
|
-
|
|
1179
|
+
/**
|
|
1180
|
+
* `token` names the moment being drawn, and is passed on to
|
|
1181
|
+
* `readGeometry` so that the overlay, which draws the same moment,
|
|
1182
|
+
* measures it only once between them.
|
|
1183
|
+
*/
|
|
1184
|
+
render(token) {
|
|
1024
1185
|
if (!this.dom || !this.canvas || !this.inner) {
|
|
1025
1186
|
return;
|
|
1026
1187
|
}
|
|
@@ -1051,9 +1212,11 @@ const minimapClass = ViewPlugin.fromClass(
|
|
|
1051
1212
|
}
|
|
1052
1213
|
context.globalAlpha = 1;
|
|
1053
1214
|
const { charWidth, lineHeight } = this.text.measure(context);
|
|
1215
|
+
setRowHeight(this.view, lineHeight);
|
|
1054
1216
|
let { startIndex, endIndex, offsetY } = this.canvasStartAndEndIndex(
|
|
1055
1217
|
context,
|
|
1056
|
-
lineHeight
|
|
1218
|
+
lineHeight,
|
|
1219
|
+
token
|
|
1057
1220
|
);
|
|
1058
1221
|
const gutters = this.view.state.facet(Config).gutters;
|
|
1059
1222
|
const lineCount = this.view.state.field(LinesState).length;
|
|
@@ -1081,27 +1244,19 @@ const minimapClass = ViewPlugin.fromClass(
|
|
|
1081
1244
|
}
|
|
1082
1245
|
context.restore();
|
|
1083
1246
|
}
|
|
1084
|
-
canvasStartAndEndIndex(context, lineHeight) {
|
|
1085
|
-
const
|
|
1086
|
-
const pTop = rawTop / Scale.SizeRatio;
|
|
1087
|
-
const pBottom = rawBottom / Scale.SizeRatio;
|
|
1247
|
+
canvasStartAndEndIndex(context, lineHeight, token) {
|
|
1248
|
+
const pTop = this.view.documentPadding.top / Scale.SizeRatio;
|
|
1088
1249
|
const canvasHeight = context.canvas.height;
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
scrollPercent = 0;
|
|
1250
|
+
const canvasTop = readGeometry(this.view, token).scrollTop * Scale.PixelMultiplier;
|
|
1251
|
+
if (lineHeight <= 0) {
|
|
1252
|
+
return { startIndex: 0, endIndex: 0, offsetY: 0 };
|
|
1093
1253
|
}
|
|
1094
|
-
const
|
|
1095
|
-
const totalHeight = pTop + pBottom + lineCount * lineHeight;
|
|
1096
|
-
const canvasTop = Math.max(
|
|
1254
|
+
const startIndex = Math.max(
|
|
1097
1255
|
0,
|
|
1098
|
-
|
|
1099
|
-
);
|
|
1100
|
-
const offsetY = Math.max(0, pTop - canvasTop);
|
|
1101
|
-
const startIndex = Math.round(
|
|
1102
|
-
Math.max(0, canvasTop - pTop) / lineHeight
|
|
1256
|
+
Math.floor((canvasTop - pTop) / lineHeight)
|
|
1103
1257
|
);
|
|
1104
|
-
const
|
|
1258
|
+
const offsetY = pTop + startIndex * lineHeight - canvasTop;
|
|
1259
|
+
const spaceForLines = Math.ceil(
|
|
1105
1260
|
(canvasHeight - offsetY) / lineHeight
|
|
1106
1261
|
);
|
|
1107
1262
|
return {
|
|
@@ -1128,8 +1283,8 @@ const minimapClass = ViewPlugin.fromClass(
|
|
|
1128
1283
|
},
|
|
1129
1284
|
{
|
|
1130
1285
|
eventHandlers: {
|
|
1131
|
-
scroll() {
|
|
1132
|
-
requestAnimationFrame(() => this.render());
|
|
1286
|
+
scroll(event) {
|
|
1287
|
+
requestAnimationFrame(() => this.render(event));
|
|
1133
1288
|
}
|
|
1134
1289
|
},
|
|
1135
1290
|
provide: (plugin) => {
|