@lvce-editor/editor-worker 16.2.0 → 16.3.0
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 +148 -109
- package/package.json +1 -1
package/dist/editorWorkerMain.js
CHANGED
|
@@ -2619,6 +2619,108 @@ const getIncrementalEdits = async (oldState, newState) => {
|
|
|
2619
2619
|
return emptyIncrementalEdits;
|
|
2620
2620
|
};
|
|
2621
2621
|
|
|
2622
|
+
/**
|
|
2623
|
+
* Gets all regex matches for a given text and regex pattern
|
|
2624
|
+
* @param text The text to match against
|
|
2625
|
+
* @param regex The regex pattern to use (should have global flag)
|
|
2626
|
+
* @returns Array of regex matches
|
|
2627
|
+
*/
|
|
2628
|
+
const getRegexMatches = (text, regex) => {
|
|
2629
|
+
return [...text.matchAll(regex)];
|
|
2630
|
+
};
|
|
2631
|
+
|
|
2632
|
+
// URL matching regex pattern - matches common URL schemes
|
|
2633
|
+
// Supports: http://, https://, ftp://, ftps://, file://
|
|
2634
|
+
// Also matches URLs without explicit scheme (www.example.com)
|
|
2635
|
+
const URL_PATTERN = /(?:(?:https?|ftp|ftps|file):\/\/)?(?:www\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\/[^\s]*)?/g;
|
|
2636
|
+
|
|
2637
|
+
// Regex to check if URL has a scheme (http://, https://, ftp://, etc.)
|
|
2638
|
+
const HAS_SCHEME_PATTERN = /^(?:https?|ftp|ftps|file):\/\//;
|
|
2639
|
+
|
|
2640
|
+
// Regex to check if URL starts with www.
|
|
2641
|
+
const HAS_WWW_PATTERN = /^www\./;
|
|
2642
|
+
|
|
2643
|
+
/**
|
|
2644
|
+
* Detects links in a given text and returns their positions
|
|
2645
|
+
* @param text The text to scan for links
|
|
2646
|
+
* @returns Array of links with their start position and length
|
|
2647
|
+
*/
|
|
2648
|
+
const detectLinks = text => {
|
|
2649
|
+
const matches = getRegexMatches(text, URL_PATTERN);
|
|
2650
|
+
const links = [];
|
|
2651
|
+
for (const match of matches) {
|
|
2652
|
+
const url = match[0];
|
|
2653
|
+
// Only consider as link if it has a scheme or starts with www.
|
|
2654
|
+
if (HAS_SCHEME_PATTERN.test(url) || HAS_WWW_PATTERN.test(url)) {
|
|
2655
|
+
links.push({
|
|
2656
|
+
length: url.length,
|
|
2657
|
+
start: match.index ?? 0
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
return links;
|
|
2662
|
+
};
|
|
2663
|
+
|
|
2664
|
+
/**
|
|
2665
|
+
* Detects all links in an editor and returns them as decorations
|
|
2666
|
+
* @param editor The editor containing lines to scan
|
|
2667
|
+
* @returns Flat array of decorations in format [offset, length, type, modifiers, ...]
|
|
2668
|
+
*/
|
|
2669
|
+
const detectAllLinksAsDecorations = editor => {
|
|
2670
|
+
const decorations = [];
|
|
2671
|
+
const {
|
|
2672
|
+
lines
|
|
2673
|
+
} = editor;
|
|
2674
|
+
let offset = 0;
|
|
2675
|
+
for (const line of lines) {
|
|
2676
|
+
const links = detectLinks(line);
|
|
2677
|
+
for (const link of links) {
|
|
2678
|
+
const linkOffset = offset + link.start;
|
|
2679
|
+
// Add link decoration: offset, length, type, modifiers
|
|
2680
|
+
decorations.push(linkOffset, link.length, Link, 0);
|
|
2681
|
+
}
|
|
2682
|
+
offset += line.length + 1; // +1 for newline
|
|
2683
|
+
}
|
|
2684
|
+
return decorations;
|
|
2685
|
+
};
|
|
2686
|
+
|
|
2687
|
+
/**
|
|
2688
|
+
* Gets the URL text at a given offset in the editor if it's a link
|
|
2689
|
+
* @param editor The editor
|
|
2690
|
+
* @param offset The offset in the document
|
|
2691
|
+
* @returns The URL string if the offset is on a link, or undefined
|
|
2692
|
+
*/
|
|
2693
|
+
const getUrlAtOffset = (editor, offset) => {
|
|
2694
|
+
const {
|
|
2695
|
+
decorations,
|
|
2696
|
+
lines
|
|
2697
|
+
} = editor;
|
|
2698
|
+
|
|
2699
|
+
// Iterate through decorations in groups of 4 (offset, length, type, modifiers)
|
|
2700
|
+
for (let i = 0; i < decorations.length; i += 4) {
|
|
2701
|
+
const decorationOffset = decorations[i];
|
|
2702
|
+
const decorationLength = decorations[i + 1];
|
|
2703
|
+
const decorationType = decorations[i + 2];
|
|
2704
|
+
|
|
2705
|
+
// Check if this decoration is a link and if the offset falls within it
|
|
2706
|
+
if (decorationType === Link && offset >= decorationOffset && offset < decorationOffset + decorationLength) {
|
|
2707
|
+
// Extract the URL text from the editor content
|
|
2708
|
+
let currentOffset = 0;
|
|
2709
|
+
for (const line of lines) {
|
|
2710
|
+
const lineLength = line.length + 1; // +1 for newline
|
|
2711
|
+
if (currentOffset + lineLength > decorationOffset) {
|
|
2712
|
+
// The link starts in this line
|
|
2713
|
+
const linkStartInLine = decorationOffset - currentOffset;
|
|
2714
|
+
const url = line.slice(linkStartInLine, linkStartInLine + decorationLength);
|
|
2715
|
+
return url;
|
|
2716
|
+
}
|
|
2717
|
+
currentOffset += lineLength;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
return undefined;
|
|
2722
|
+
};
|
|
2723
|
+
|
|
2622
2724
|
const splitLines = lines => {
|
|
2623
2725
|
if (!lines) {
|
|
2624
2726
|
return [''];
|
|
@@ -3034,11 +3136,17 @@ const scheduleDocumentAndCursorsSelections = async (editor, changes, selectionCh
|
|
|
3034
3136
|
selections: newSelections,
|
|
3035
3137
|
undoStack: [...editor.undoStack, changes]
|
|
3036
3138
|
};
|
|
3037
|
-
|
|
3038
|
-
const
|
|
3039
|
-
const
|
|
3040
|
-
const newEditor2 = {
|
|
3139
|
+
// Update link decorations after text changes
|
|
3140
|
+
const linkDecorations = detectAllLinksAsDecorations(newEditor);
|
|
3141
|
+
const newEditorWithDecorations = {
|
|
3041
3142
|
...newEditor,
|
|
3143
|
+
decorations: linkDecorations
|
|
3144
|
+
};
|
|
3145
|
+
set$6(editor.uid, editor, newEditorWithDecorations);
|
|
3146
|
+
const incrementalEdits = await getIncrementalEdits(editor, newEditorWithDecorations);
|
|
3147
|
+
const editorWithNewWidgets = await applyWidgetChanges(newEditorWithDecorations, changes);
|
|
3148
|
+
const newEditor2 = {
|
|
3149
|
+
...newEditorWithDecorations,
|
|
3042
3150
|
...editorWithNewWidgets,
|
|
3043
3151
|
incrementalEdits
|
|
3044
3152
|
};
|
|
@@ -3308,108 +3416,6 @@ const getLanguages = async (platform, assetDir) => {
|
|
|
3308
3416
|
return languages;
|
|
3309
3417
|
};
|
|
3310
3418
|
|
|
3311
|
-
/**
|
|
3312
|
-
* Gets all regex matches for a given text and regex pattern
|
|
3313
|
-
* @param text The text to match against
|
|
3314
|
-
* @param regex The regex pattern to use (should have global flag)
|
|
3315
|
-
* @returns Array of regex matches
|
|
3316
|
-
*/
|
|
3317
|
-
const getRegexMatches = (text, regex) => {
|
|
3318
|
-
return [...text.matchAll(regex)];
|
|
3319
|
-
};
|
|
3320
|
-
|
|
3321
|
-
// URL matching regex pattern - matches common URL schemes
|
|
3322
|
-
// Supports: http://, https://, ftp://, ftps://, file://
|
|
3323
|
-
// Also matches URLs without explicit scheme (www.example.com)
|
|
3324
|
-
const URL_PATTERN = /(?:(?:https?|ftp|ftps|file):\/\/)?(?:www\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\/[^\s]*)?/g;
|
|
3325
|
-
|
|
3326
|
-
// Regex to check if URL has a scheme (http://, https://, ftp://, etc.)
|
|
3327
|
-
const HAS_SCHEME_PATTERN = /^(?:https?|ftp|ftps|file):\/\//;
|
|
3328
|
-
|
|
3329
|
-
// Regex to check if URL starts with www.
|
|
3330
|
-
const HAS_WWW_PATTERN = /^www\./;
|
|
3331
|
-
|
|
3332
|
-
/**
|
|
3333
|
-
* Detects links in a given text and returns their positions
|
|
3334
|
-
* @param text The text to scan for links
|
|
3335
|
-
* @returns Array of links with their start position and length
|
|
3336
|
-
*/
|
|
3337
|
-
const detectLinks = text => {
|
|
3338
|
-
const matches = getRegexMatches(text, URL_PATTERN);
|
|
3339
|
-
const links = [];
|
|
3340
|
-
for (const match of matches) {
|
|
3341
|
-
const url = match[0];
|
|
3342
|
-
// Only consider as link if it has a scheme or starts with www.
|
|
3343
|
-
if (HAS_SCHEME_PATTERN.test(url) || HAS_WWW_PATTERN.test(url)) {
|
|
3344
|
-
links.push({
|
|
3345
|
-
length: url.length,
|
|
3346
|
-
start: match.index ?? 0
|
|
3347
|
-
});
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
|
-
return links;
|
|
3351
|
-
};
|
|
3352
|
-
|
|
3353
|
-
/**
|
|
3354
|
-
* Detects all links in an editor and returns them as decorations
|
|
3355
|
-
* @param editor The editor containing lines to scan
|
|
3356
|
-
* @returns Flat array of decorations in format [offset, length, type, modifiers, ...]
|
|
3357
|
-
*/
|
|
3358
|
-
const detectAllLinksAsDecorations = editor => {
|
|
3359
|
-
const decorations = [];
|
|
3360
|
-
const {
|
|
3361
|
-
lines
|
|
3362
|
-
} = editor;
|
|
3363
|
-
let offset = 0;
|
|
3364
|
-
for (const line of lines) {
|
|
3365
|
-
const links = detectLinks(line);
|
|
3366
|
-
for (const link of links) {
|
|
3367
|
-
const linkOffset = offset + link.start;
|
|
3368
|
-
// Add link decoration: offset, length, type, modifiers
|
|
3369
|
-
decorations.push(linkOffset, link.length, Link, 0);
|
|
3370
|
-
}
|
|
3371
|
-
offset += line.length + 1; // +1 for newline
|
|
3372
|
-
}
|
|
3373
|
-
return decorations;
|
|
3374
|
-
};
|
|
3375
|
-
|
|
3376
|
-
/**
|
|
3377
|
-
* Gets the URL text at a given offset in the editor if it's a link
|
|
3378
|
-
* @param editor The editor
|
|
3379
|
-
* @param offset The offset in the document
|
|
3380
|
-
* @returns The URL string if the offset is on a link, or undefined
|
|
3381
|
-
*/
|
|
3382
|
-
const getUrlAtOffset = (editor, offset) => {
|
|
3383
|
-
const {
|
|
3384
|
-
decorations,
|
|
3385
|
-
lines
|
|
3386
|
-
} = editor;
|
|
3387
|
-
|
|
3388
|
-
// Iterate through decorations in groups of 4 (offset, length, type, modifiers)
|
|
3389
|
-
for (let i = 0; i < decorations.length; i += 4) {
|
|
3390
|
-
const decorationOffset = decorations[i];
|
|
3391
|
-
const decorationLength = decorations[i + 1];
|
|
3392
|
-
const decorationType = decorations[i + 2];
|
|
3393
|
-
|
|
3394
|
-
// Check if this decoration is a link and if the offset falls within it
|
|
3395
|
-
if (decorationType === Link && offset >= decorationOffset && offset < decorationOffset + decorationLength) {
|
|
3396
|
-
// Extract the URL text from the editor content
|
|
3397
|
-
let currentOffset = 0;
|
|
3398
|
-
for (const line of lines) {
|
|
3399
|
-
const lineLength = line.length + 1; // +1 for newline
|
|
3400
|
-
if (currentOffset + lineLength > decorationOffset) {
|
|
3401
|
-
// The link starts in this line
|
|
3402
|
-
const linkStartInLine = decorationOffset - currentOffset;
|
|
3403
|
-
const url = line.slice(linkStartInLine, linkStartInLine + decorationLength);
|
|
3404
|
-
return url;
|
|
3405
|
-
}
|
|
3406
|
-
currentOffset += lineLength;
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
|
-
return undefined;
|
|
3411
|
-
};
|
|
3412
|
-
|
|
3413
3419
|
const measureCharacterWidth = async (fontWeight, fontSize, fontFamily, letterSpacing) => {
|
|
3414
3420
|
return await measureTextWidth('a', fontWeight, fontSize, fontFamily, letterSpacing, false, 0);
|
|
3415
3421
|
};
|
|
@@ -3497,6 +3503,37 @@ const getVisibleDiagnostics = async (editor, diagnostics) => {
|
|
|
3497
3503
|
return visibleDiagnostics;
|
|
3498
3504
|
};
|
|
3499
3505
|
|
|
3506
|
+
/**
|
|
3507
|
+
* Merges link decorations with diagnostic decorations
|
|
3508
|
+
* Links should always be present, but we also need to include any diagnostic decorations
|
|
3509
|
+
*/
|
|
3510
|
+
const mergeLinksWithDiagnosticDecorations = (editor, diagnosticDecorations) => {
|
|
3511
|
+
// Get link decorations
|
|
3512
|
+
const linkDecorations = detectAllLinksAsDecorations(editor);
|
|
3513
|
+
|
|
3514
|
+
// Merge with diagnostic decorations
|
|
3515
|
+
const allDecorations = [...linkDecorations, ...diagnosticDecorations];
|
|
3516
|
+
|
|
3517
|
+
// Sort by offset to maintain proper order
|
|
3518
|
+
const sortedDecorations = [];
|
|
3519
|
+
for (let i = 0; i < allDecorations.length; i += 4) {
|
|
3520
|
+
sortedDecorations.push({
|
|
3521
|
+
length: allDecorations[i + 1],
|
|
3522
|
+
modifiers: allDecorations[i + 3],
|
|
3523
|
+
offset: allDecorations[i],
|
|
3524
|
+
type: allDecorations[i + 2]
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
sortedDecorations.sort((a, b) => a.offset - b.offset);
|
|
3528
|
+
|
|
3529
|
+
// Flatten back to array format
|
|
3530
|
+
const result = [];
|
|
3531
|
+
for (const dec of sortedDecorations) {
|
|
3532
|
+
result.push(dec.offset, dec.length, dec.type, dec.modifiers);
|
|
3533
|
+
}
|
|
3534
|
+
return result;
|
|
3535
|
+
};
|
|
3536
|
+
|
|
3500
3537
|
const updateDiagnostics = async newState => {
|
|
3501
3538
|
try {
|
|
3502
3539
|
// TODO handle error
|
|
@@ -3517,11 +3554,13 @@ const updateDiagnostics = async newState => {
|
|
|
3517
3554
|
return newState;
|
|
3518
3555
|
}
|
|
3519
3556
|
const visualDecorations = await getVisibleDiagnostics(latest.newState, diagnostics);
|
|
3520
|
-
//
|
|
3521
|
-
const
|
|
3557
|
+
// Get diagnostic decorations from visual decorations (if any)
|
|
3558
|
+
const diagnosticDecorations = visualDecorations.flatMap(deco => [deco.offset, deco.length, deco.type, deco.modifiers || 0]);
|
|
3559
|
+
// Merge link decorations with diagnostic decorations
|
|
3560
|
+
const mergedDecorations = mergeLinksWithDiagnosticDecorations(latest.newState, diagnosticDecorations);
|
|
3522
3561
|
const newEditor = {
|
|
3523
3562
|
...latest.newState,
|
|
3524
|
-
decorations:
|
|
3563
|
+
decorations: mergedDecorations,
|
|
3525
3564
|
// Text-level decorations (flat array) for CSS classes
|
|
3526
3565
|
diagnostics,
|
|
3527
3566
|
visualDecorations // Visual decorations (objects with x, y, width, height) for squiggly underlines
|