@lvce-editor/editor-worker 16.1.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 +163 -74
- 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,71 +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
3419
|
const measureCharacterWidth = async (fontWeight, fontSize, fontFamily, letterSpacing) => {
|
|
3377
3420
|
return await measureTextWidth('a', fontWeight, fontSize, fontFamily, letterSpacing, false, 0);
|
|
3378
3421
|
};
|
|
@@ -3460,6 +3503,37 @@ const getVisibleDiagnostics = async (editor, diagnostics) => {
|
|
|
3460
3503
|
return visibleDiagnostics;
|
|
3461
3504
|
};
|
|
3462
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
|
+
|
|
3463
3537
|
const updateDiagnostics = async newState => {
|
|
3464
3538
|
try {
|
|
3465
3539
|
// TODO handle error
|
|
@@ -3480,11 +3554,13 @@ const updateDiagnostics = async newState => {
|
|
|
3480
3554
|
return newState;
|
|
3481
3555
|
}
|
|
3482
3556
|
const visualDecorations = await getVisibleDiagnostics(latest.newState, diagnostics);
|
|
3483
|
-
//
|
|
3484
|
-
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);
|
|
3485
3561
|
const newEditor = {
|
|
3486
3562
|
...latest.newState,
|
|
3487
|
-
decorations:
|
|
3563
|
+
decorations: mergedDecorations,
|
|
3488
3564
|
// Text-level decorations (flat array) for CSS classes
|
|
3489
3565
|
diagnostics,
|
|
3490
3566
|
visualDecorations // Visual decorations (objects with x, y, width, height) for squiggly underlines
|
|
@@ -5440,6 +5516,10 @@ const setPosition$1 = position => {
|
|
|
5440
5516
|
state$3.position = position;
|
|
5441
5517
|
};
|
|
5442
5518
|
|
|
5519
|
+
const openExternal = async url => {
|
|
5520
|
+
await invoke$b('Open.openUrl', url);
|
|
5521
|
+
};
|
|
5522
|
+
|
|
5443
5523
|
// TODO first change cursor position, then run go to definition
|
|
5444
5524
|
// cursor should appear at mousedown position immediately
|
|
5445
5525
|
const handleSingleClickWithAlt = async (editor, position) => {
|
|
@@ -5447,12 +5527,21 @@ const handleSingleClickWithAlt = async (editor, position) => {
|
|
|
5447
5527
|
columnIndex,
|
|
5448
5528
|
rowIndex
|
|
5449
5529
|
} = position;
|
|
5530
|
+
|
|
5531
|
+
// Check if the click is on a link
|
|
5532
|
+
const offset = offsetAt(editor, rowIndex, columnIndex);
|
|
5533
|
+
const url = getUrlAtOffset(editor, offset);
|
|
5534
|
+
if (url) {
|
|
5535
|
+
// Open the link
|
|
5536
|
+
await openExternal(url);
|
|
5537
|
+
return editor;
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
// Otherwise, perform the default go to definition
|
|
5450
5541
|
const newEditor = {
|
|
5451
5542
|
...editor,
|
|
5452
5543
|
selections: new Uint32Array([rowIndex, columnIndex, rowIndex, columnIndex])
|
|
5453
5544
|
};
|
|
5454
|
-
// TODO rectangular selection with alt click,
|
|
5455
|
-
// but also go to definition with alt click
|
|
5456
5545
|
const newEditor2 = await goToDefinition(newEditor);
|
|
5457
5546
|
return newEditor2;
|
|
5458
5547
|
};
|