@lvce-editor/editor-worker 19.72.4 → 19.72.6
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/dist/editorWorkerMain.js +222 -64
- package/package.json +1 -1
package/dist/editorWorkerMain.js
CHANGED
|
@@ -2463,77 +2463,132 @@ const getHorizontalVisibleRange = (line, deltaX, width, averageCharWidth, tabSiz
|
|
|
2463
2463
|
start
|
|
2464
2464
|
};
|
|
2465
2465
|
};
|
|
2466
|
-
const
|
|
2467
|
-
const fallback =
|
|
2468
|
-
const range = getHorizontalVisibleRange(line, deltaX, width, averageCharWidth, tabSize, threshold);
|
|
2466
|
+
const getHorizontalVisibleRangesMeasured = async (requests, measureWidths) => {
|
|
2467
|
+
const fallback = request => {
|
|
2468
|
+
const range = getHorizontalVisibleRange(request.line, request.deltaX, request.width, request.averageCharWidth, request.tabSize, request.threshold);
|
|
2469
2469
|
return {
|
|
2470
2470
|
...range,
|
|
2471
|
-
difference: getHorizontalVisibleDifference(line, range.start, deltaX, averageCharWidth, tabSize)
|
|
2471
|
+
difference: getHorizontalVisibleDifference(request.line, range.start, request.deltaX, request.averageCharWidth, request.tabSize)
|
|
2472
2472
|
};
|
|
2473
2473
|
};
|
|
2474
|
-
|
|
2475
|
-
|
|
2474
|
+
const results = requests.map(request => fallback(request));
|
|
2475
|
+
const boundariesByLine = [];
|
|
2476
|
+
const measurementsByLine = [];
|
|
2477
|
+
const cursors = [];
|
|
2478
|
+
for (let lineIndex = 0; lineIndex < requests.length; lineIndex++) {
|
|
2479
|
+
const request = requests[lineIndex];
|
|
2480
|
+
if (request.line.length <= request.threshold) {
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
const segments = getSegments(request.line);
|
|
2484
|
+
const boundaries = [0, ...segments.map(({
|
|
2485
|
+
index,
|
|
2486
|
+
segment
|
|
2487
|
+
}) => index + segment.length)];
|
|
2488
|
+
boundariesByLine[lineIndex] = boundaries;
|
|
2489
|
+
measurementsByLine[lineIndex] = new Map([[0, 0]]);
|
|
2490
|
+
const scrollOffset = Math.max(0, Number.isFinite(request.deltaX) ? request.deltaX : 0);
|
|
2491
|
+
const viewportWidth = Number.isFinite(request.width) ? Math.max(0, request.width) : Infinity;
|
|
2492
|
+
cursors.push({
|
|
2493
|
+
high: boundaries.length,
|
|
2494
|
+
isStart: true,
|
|
2495
|
+
lineIndex,
|
|
2496
|
+
low: 0,
|
|
2497
|
+
target: scrollOffset
|
|
2498
|
+
});
|
|
2499
|
+
cursors.push({
|
|
2500
|
+
high: boundaries.length,
|
|
2501
|
+
isStart: false,
|
|
2502
|
+
lineIndex,
|
|
2503
|
+
low: 0,
|
|
2504
|
+
target: scrollOffset + viewportWidth
|
|
2505
|
+
});
|
|
2476
2506
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
index,
|
|
2482
|
-
segment
|
|
2483
|
-
}) => index + segment.length)];
|
|
2484
|
-
const measurements = new Map([[0, 0]]);
|
|
2485
|
-
const measureBoundary = async boundaryIndex => {
|
|
2486
|
-
const cached = measurements.get(boundaryIndex);
|
|
2487
|
-
if (cached !== undefined) {
|
|
2488
|
-
return cached;
|
|
2489
|
-
}
|
|
2490
|
-
const prefix = line.slice(0, boundaries[boundaryIndex]).replaceAll('\t', () => ' '.repeat(tabSize));
|
|
2491
|
-
const measured = await measureWidth(prefix);
|
|
2492
|
-
measurements.set(boundaryIndex, measured);
|
|
2493
|
-
return measured;
|
|
2494
|
-
};
|
|
2495
|
-
const findGreatestBoundaryAtMost = async target => {
|
|
2496
|
-
let low = 0;
|
|
2497
|
-
let high = boundaries.length;
|
|
2498
|
-
while (low < high) {
|
|
2499
|
-
const middle = Math.floor((low + high) / 2);
|
|
2500
|
-
if ((await measureBoundary(middle)) <= target) {
|
|
2501
|
-
low = middle + 1;
|
|
2502
|
-
} else {
|
|
2503
|
-
high = middle;
|
|
2507
|
+
try {
|
|
2508
|
+
const addPendingMeasurement = (cursor, pending) => {
|
|
2509
|
+
if (cursor.low >= cursor.high) {
|
|
2510
|
+
return;
|
|
2504
2511
|
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2512
|
+
const boundaryIndex = Math.floor((cursor.low + cursor.high) / 2);
|
|
2513
|
+
if (measurementsByLine[cursor.lineIndex].has(boundaryIndex)) {
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
const request = requests[cursor.lineIndex];
|
|
2517
|
+
const prefix = request.line.slice(0, boundariesByLine[cursor.lineIndex][boundaryIndex]).replaceAll('\t', () => ' '.repeat(request.tabSize));
|
|
2518
|
+
pending.set(`${cursor.lineIndex}:${boundaryIndex}`, {
|
|
2519
|
+
boundaryIndex,
|
|
2520
|
+
lineIndex: cursor.lineIndex,
|
|
2521
|
+
text: prefix
|
|
2522
|
+
});
|
|
2523
|
+
};
|
|
2524
|
+
const updateCursor = cursor => {
|
|
2525
|
+
if (cursor.low >= cursor.high) {
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
const middle = Math.floor((cursor.low + cursor.high) / 2);
|
|
2529
|
+
const measured = measurementsByLine[cursor.lineIndex].get(middle);
|
|
2530
|
+
if (measured === undefined) {
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
if (cursor.isStart ? measured <= cursor.target : measured < cursor.target) {
|
|
2534
|
+
cursor.low = middle + 1;
|
|
2515
2535
|
} else {
|
|
2516
|
-
high = middle;
|
|
2536
|
+
cursor.high = middle;
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
while (cursors.some(({
|
|
2540
|
+
high,
|
|
2541
|
+
low
|
|
2542
|
+
}) => low < high)) {
|
|
2543
|
+
const pending = new Map();
|
|
2544
|
+
for (const cursor of cursors) {
|
|
2545
|
+
addPendingMeasurement(cursor, pending);
|
|
2546
|
+
}
|
|
2547
|
+
if (pending.size > 0) {
|
|
2548
|
+
const entries = pending.values().toArray();
|
|
2549
|
+
const measuredWidths = await measureWidths(entries.map(({
|
|
2550
|
+
text
|
|
2551
|
+
}) => text));
|
|
2552
|
+
if (measuredWidths.length !== entries.length) {
|
|
2553
|
+
throw new Error('Text measurement returned an unexpected number of widths');
|
|
2554
|
+
}
|
|
2555
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2556
|
+
const {
|
|
2557
|
+
boundaryIndex,
|
|
2558
|
+
lineIndex
|
|
2559
|
+
} = entries[i];
|
|
2560
|
+
measurementsByLine[lineIndex].set(boundaryIndex, measuredWidths[i]);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
for (const cursor of cursors) {
|
|
2564
|
+
updateCursor(cursor);
|
|
2517
2565
|
}
|
|
2518
2566
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2567
|
+
for (let lineIndex = 0; lineIndex < boundariesByLine.length; lineIndex++) {
|
|
2568
|
+
const boundaries = boundariesByLine[lineIndex];
|
|
2569
|
+
if (!boundaries) {
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
const request = requests[lineIndex];
|
|
2573
|
+
const lineCursors = cursors.filter(cursor => cursor.lineIndex === lineIndex);
|
|
2574
|
+
const startBoundary = Math.max(0, lineCursors[0].low - 1);
|
|
2575
|
+
const endBoundary = Math.min(boundaries.length - 1, lineCursors[1].low);
|
|
2576
|
+
const start = boundaries[startBoundary];
|
|
2577
|
+
let end = boundaries[endBoundary];
|
|
2578
|
+
if (end < request.line.length) {
|
|
2579
|
+
end = boundaries[Math.min(boundaries.length - 1, endBoundary + 1)];
|
|
2580
|
+
}
|
|
2581
|
+
const scrollOffset = Math.max(0, Number.isFinite(request.deltaX) ? request.deltaX : 0);
|
|
2582
|
+
results[lineIndex] = {
|
|
2583
|
+
difference: (measurementsByLine[lineIndex].get(startBoundary) ?? 0) - scrollOffset,
|
|
2584
|
+
end,
|
|
2585
|
+
start
|
|
2586
|
+
};
|
|
2528
2587
|
}
|
|
2529
|
-
return {
|
|
2530
|
-
difference: (await measureBoundary(startBoundary)) - scrollOffset,
|
|
2531
|
-
end,
|
|
2532
|
-
start
|
|
2533
|
-
};
|
|
2534
2588
|
} catch {
|
|
2535
|
-
|
|
2589
|
+
// Keep the existing approximate range fallback when worker measurement fails.
|
|
2536
2590
|
}
|
|
2591
|
+
return results;
|
|
2537
2592
|
};
|
|
2538
2593
|
const getHorizontalVisibleDifference = (line, start, deltaX, averageCharWidth, tabSize) => {
|
|
2539
2594
|
const charWidth = averageCharWidth > 0 ? averageCharWidth : 1;
|
|
@@ -2953,6 +3008,10 @@ const measureTextWidthSlow = async (text, fontWeight, fontSize, fontFamily, lett
|
|
|
2953
3008
|
return width;
|
|
2954
3009
|
};
|
|
2955
3010
|
|
|
3011
|
+
const measureTextWidthsSlow = async (texts, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
|
|
3012
|
+
return await invoke$e('TextMeasurement.measureTextWidths', texts, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
|
|
3013
|
+
};
|
|
3014
|
+
|
|
2956
3015
|
const measureTextWidth = async (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
|
|
2957
3016
|
// TODO maybe have a property for the whole text document
|
|
2958
3017
|
// whether the document is ASCII or not
|
|
@@ -2963,6 +3022,29 @@ const measureTextWidth = async (text, fontWeight, fontSize, fontFamily, letterSp
|
|
|
2963
3022
|
}
|
|
2964
3023
|
return await measureTextWidthSlow(text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
|
|
2965
3024
|
};
|
|
3025
|
+
const measureTextWidths = async (texts, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
|
|
3026
|
+
const widths = Array.from({
|
|
3027
|
+
length: texts.length
|
|
3028
|
+
}, (_, index) => index);
|
|
3029
|
+
const slowTexts = [];
|
|
3030
|
+
const slowIndexes = [];
|
|
3031
|
+
for (let i = 0; i < texts.length; i++) {
|
|
3032
|
+
const text = texts[i];
|
|
3033
|
+
if (isMonoSpaceFont && isAscii(text)) {
|
|
3034
|
+
widths[i] = await measureTextWidthFast(text, charWidth);
|
|
3035
|
+
} else {
|
|
3036
|
+
slowTexts.push(text);
|
|
3037
|
+
slowIndexes.push(i);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
if (slowTexts.length > 0) {
|
|
3041
|
+
const slowWidths = await measureTextWidthsSlow(slowTexts, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth);
|
|
3042
|
+
for (let i = 0; i < slowWidths.length; i++) {
|
|
3043
|
+
widths[slowIndexes[i]] = slowWidths[i];
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
return widths;
|
|
3047
|
+
};
|
|
2966
3048
|
|
|
2967
3049
|
const Dot = '.';
|
|
2968
3050
|
const DoubleQuote$1 = '"';
|
|
@@ -3487,10 +3569,18 @@ const getLineInfosViewport = async (editor, tokens, embeddedResults, minLineY, m
|
|
|
3487
3569
|
const tokenMap = get$5(languageId);
|
|
3488
3570
|
let offset = minLineOffset;
|
|
3489
3571
|
const tabSize = editor.tabSize ?? 2;
|
|
3572
|
+
const measuredRanges = await getHorizontalVisibleRangesMeasured(lines.slice(minLineY, maxLineY).map(line => ({
|
|
3573
|
+
averageCharWidth,
|
|
3574
|
+
deltaX,
|
|
3575
|
+
line,
|
|
3576
|
+
tabSize,
|
|
3577
|
+
threshold: horizontalVirtualizationThreshold,
|
|
3578
|
+
width
|
|
3579
|
+
})), texts => measureTextWidths(texts, editor.fontWeight, editor.fontSize, editor.fontFamily, editor.letterSpacing, editor.isMonospaceFont, editor.charWidth));
|
|
3490
3580
|
for (let i = minLineY; i < maxLineY; i++) {
|
|
3491
3581
|
const line = lines[i];
|
|
3492
3582
|
const normalize = shouldNormalizeText(line);
|
|
3493
|
-
const measuredRange =
|
|
3583
|
+
const measuredRange = measuredRanges[i - minLineY];
|
|
3494
3584
|
|
|
3495
3585
|
// Use decorations that were pre-computed (includes links and diagnostics)
|
|
3496
3586
|
// Filter decorations to only include those for this line
|
|
@@ -5627,7 +5717,7 @@ const FocusFindWidgetToggleReplace = 42;
|
|
|
5627
5717
|
const SourceActions = 38;
|
|
5628
5718
|
const FocusCodeGenerator = 52;
|
|
5629
5719
|
|
|
5630
|
-
const createEditor2 = (id, uri, x, y, width, height, platform, assetDir, _languageId, _tokenizePath, _useCache, applicationId) => {
|
|
5720
|
+
const createEditor2 = (id, uri, x, y, width, height, platform, assetDir, _languageId, _tokenizePath, _useCache, applicationId, editorFileCacheEnabled = false) => {
|
|
5631
5721
|
number(id);
|
|
5632
5722
|
const editor = {
|
|
5633
5723
|
...(applicationId !== undefined && {
|
|
@@ -5654,6 +5744,7 @@ const createEditor2 = (id, uri, x, y, width, height, platform, assetDir, _langua
|
|
|
5654
5744
|
differences: [],
|
|
5655
5745
|
documentSymbols: [],
|
|
5656
5746
|
dragAndDropEnabled: true,
|
|
5747
|
+
editorFileCacheEnabled,
|
|
5657
5748
|
embeds: [],
|
|
5658
5749
|
endOfLine: 'lf',
|
|
5659
5750
|
endOfLineDecorations: [],
|
|
@@ -16742,11 +16833,78 @@ const intialize = async (syntaxHighlightingEnabled, syncIncremental) => {
|
|
|
16742
16833
|
};
|
|
16743
16834
|
|
|
16744
16835
|
const largeFileContentLength = 10 * 1024 * 1024;
|
|
16836
|
+
const editorFileCacheMaxBytes = 500_000;
|
|
16745
16837
|
const pendingFileReads = new Map();
|
|
16746
|
-
const
|
|
16838
|
+
const getFileStatFingerprint = stat => {
|
|
16839
|
+
if (!stat || typeof stat !== 'object' || !Number.isFinite(stat.size)) {
|
|
16840
|
+
return '';
|
|
16841
|
+
}
|
|
16842
|
+
const modifiedTime = stat.mtimeMs ?? stat.mtime;
|
|
16843
|
+
if (typeof modifiedTime !== 'number' && typeof modifiedTime !== 'string') {
|
|
16844
|
+
return '';
|
|
16845
|
+
}
|
|
16846
|
+
const fingerprint = [stat.type, stat.size, modifiedTime];
|
|
16847
|
+
const changedTime = stat.ctimeMs ?? stat.ctime;
|
|
16848
|
+
if (typeof changedTime === 'number' || typeof changedTime === 'string') {
|
|
16849
|
+
fingerprint.push(changedTime);
|
|
16850
|
+
}
|
|
16851
|
+
if (typeof stat.etag === 'string') {
|
|
16852
|
+
fingerprint.push(stat.etag);
|
|
16853
|
+
}
|
|
16854
|
+
return JSON.stringify(fingerprint);
|
|
16855
|
+
};
|
|
16856
|
+
const isExcludedFromFileCache = uri => {
|
|
16857
|
+
const path = uri.split(/[?#]/, 1)[0];
|
|
16858
|
+
const fileName = path.slice(path.lastIndexOf('/') + 1);
|
|
16859
|
+
return fileName === '.env' || fileName.startsWith('.env.');
|
|
16860
|
+
};
|
|
16861
|
+
const removeCachedEditorFile = (applicationId, uri) => {
|
|
16862
|
+
void invoke$9(undefined, 'CacheStorage.removeEditorFileCache', applicationId || '', uri).catch(() => {});
|
|
16863
|
+
};
|
|
16864
|
+
const readFileWithCache = async (applicationId, uri, forceReload, useCache) => {
|
|
16865
|
+
if (!useCache) {
|
|
16866
|
+
return readFile$1(applicationId, uri);
|
|
16867
|
+
}
|
|
16747
16868
|
if (forceReload) {
|
|
16869
|
+
removeCachedEditorFile(applicationId, uri);
|
|
16870
|
+
return readFile$1(applicationId, uri);
|
|
16871
|
+
}
|
|
16872
|
+
if (isExcludedFromFileCache(uri)) {
|
|
16873
|
+
removeCachedEditorFile(applicationId, uri);
|
|
16748
16874
|
return readFile$1(applicationId, uri);
|
|
16749
16875
|
}
|
|
16876
|
+
let fingerprint = '';
|
|
16877
|
+
try {
|
|
16878
|
+
const stat = await invoke$9(applicationId, 'FileSystem.statWithMetadata', uri);
|
|
16879
|
+
if (stat?.size <= editorFileCacheMaxBytes) {
|
|
16880
|
+
fingerprint = getFileStatFingerprint(stat);
|
|
16881
|
+
}
|
|
16882
|
+
} catch {
|
|
16883
|
+
// Filesystems without stat metadata continue to use normal reads.
|
|
16884
|
+
}
|
|
16885
|
+
if (!fingerprint) {
|
|
16886
|
+
removeCachedEditorFile(applicationId, uri);
|
|
16887
|
+
}
|
|
16888
|
+
if (fingerprint) {
|
|
16889
|
+
try {
|
|
16890
|
+
const cached = await invoke$9(undefined, 'CacheStorage.getEditorFileCache', applicationId || '', uri, fingerprint);
|
|
16891
|
+
if (typeof cached === 'string') {
|
|
16892
|
+
return cached;
|
|
16893
|
+
}
|
|
16894
|
+
} catch {
|
|
16895
|
+
// Cache Storage failures must not prevent opening a file.
|
|
16896
|
+
}
|
|
16897
|
+
}
|
|
16898
|
+
const content = await readFile$1(applicationId, uri);
|
|
16899
|
+
if (fingerprint && new TextEncoder().encode(content).byteLength <= editorFileCacheMaxBytes) {
|
|
16900
|
+
void invoke$9(undefined, 'CacheStorage.setEditorFileCache', applicationId || '', uri, fingerprint, content).catch(() => {});
|
|
16901
|
+
}
|
|
16902
|
+
return content;
|
|
16903
|
+
};
|
|
16904
|
+
const readFile = (applicationId, uri, forceReload, useCache) => {
|
|
16905
|
+
if (forceReload) {
|
|
16906
|
+
return readFileWithCache(applicationId, uri, forceReload, useCache);
|
|
16907
|
+
}
|
|
16750
16908
|
let applicationReads = pendingFileReads.get(applicationId);
|
|
16751
16909
|
if (!applicationReads) {
|
|
16752
16910
|
applicationReads = new Map();
|
|
@@ -16768,7 +16926,7 @@ const readFile = (applicationId, uri, forceReload) => {
|
|
|
16768
16926
|
};
|
|
16769
16927
|
fileRead = (async () => {
|
|
16770
16928
|
try {
|
|
16771
|
-
return await
|
|
16929
|
+
return await readFileWithCache(applicationId, uri, forceReload, useCache);
|
|
16772
16930
|
} finally {
|
|
16773
16931
|
removePendingRead();
|
|
16774
16932
|
}
|
|
@@ -16927,7 +17085,7 @@ const loadContent = async (state, savedState, largeFile = false, focus = true, f
|
|
|
16927
17085
|
let endOfLine = existingEditor?.endOfLine || 'lf';
|
|
16928
17086
|
try {
|
|
16929
17087
|
if (!existingEditor) {
|
|
16930
|
-
content = await readFile(state.applicationId, uri, forceReload);
|
|
17088
|
+
content = await readFile(state.applicationId, uri, forceReload, state.editorFileCacheEnabled === true);
|
|
16931
17089
|
endOfLine = getEndOfLine(content);
|
|
16932
17090
|
content = normalizeLineEndings(content);
|
|
16933
17091
|
}
|