@nocturnium/svelte-ide 1.0.2 → 1.0.4
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 -3
- package/dist/components/ai/AIMessageContent.svelte +24 -14
- package/dist/components/ai/AIPanel.svelte +22 -0
- package/dist/components/editor/CollaborativeEditor.svelte +68 -5
- package/dist/components/editor/CollaborativeEditor.svelte.d.ts +14 -0
- package/dist/components/editor/ContextLens.svelte +16 -10
- package/dist/components/editor/CustomEditor.svelte +52 -33
- package/dist/components/editor/CustomEditor.svelte.d.ts +2 -2
- package/dist/components/editor/EchoCursorLayer.svelte +43 -11
- package/dist/components/editor/Editor.svelte +17 -0
- package/dist/components/editor/Editor.svelte.d.ts +9 -0
- package/dist/components/editor/EditorPane.svelte +18 -1
- package/dist/components/editor/EditorPane.svelte.d.ts +5 -0
- package/dist/components/editor/EditorSelections.svelte +27 -11
- package/dist/components/editor/EditorSelections.svelte.d.ts +1 -0
- package/dist/components/editor/GhostBracketLayer.svelte +38 -25
- package/dist/components/editor/core/folding.d.ts +11 -0
- package/dist/components/editor/core/folding.js +41 -0
- package/dist/components/editor/core/index.d.ts +0 -5
- package/dist/components/editor/core/index.js +4 -5
- package/dist/components/editor/core/state.d.ts +5 -0
- package/dist/components/editor/core/state.js +131 -12
- package/dist/components/editor/editor-find.d.ts +1 -0
- package/dist/components/editor/editor-find.js +6 -5
- package/dist/components/editor/editor-input.d.ts +1 -0
- package/dist/components/editor/editor-input.js +4 -1
- package/dist/components/editor/editor-scroll.d.ts +1 -0
- package/dist/components/editor/editor-scroll.js +2 -1
- package/dist/components/editor/index.d.ts +19 -3
- package/dist/components/editor/index.js +18 -4
- package/dist/components/editor/tokenizer/base.d.ts +1 -25
- package/dist/components/editor/tokenizer/base.js +0 -172
- package/dist/components/editor/tokenizer/index.d.ts +4 -0
- package/dist/components/editor/tokenizer/index.js +1 -1
- package/dist/components/editor/tokenizer/languages/html.d.ts +3 -2
- package/dist/components/editor/tokenizer/languages/html.js +64 -6
- package/dist/components/editor/tokenizer/languages/javascript.d.ts +0 -3
- package/dist/components/editor/tokenizer/languages/javascript.js +1 -2
- package/dist/components/editor/tokenizer/languages/svelte.d.ts +1 -1
- package/dist/components/editor/tokenizer/languages/svelte.js +6 -1
- package/dist/components/editor/tokenizer/types.d.ts +0 -28
- package/dist/crdt/awareness.d.ts +8 -2
- package/dist/crdt/awareness.js +11 -4
- package/dist/crdt/document.d.ts +10 -1
- package/dist/crdt/document.js +15 -7
- package/dist/crdt/index.d.ts +8 -2
- package/dist/crdt/index.js +5 -2
- package/dist/crdt/undo.d.ts +2 -7
- package/dist/crdt/undo.js +1 -8
- package/dist/index.d.ts +7 -9
- package/dist/index.js +7 -9
- package/dist/services/error-handling.d.ts +2 -11
- package/dist/services/error-handling.js +15 -4
- package/dist/services/lsp-client.d.ts +3 -0
- package/dist/services/lsp-client.js +55 -10
- package/dist/services/mock-ai.js +1 -1
- package/dist/services/optimistic.d.ts +8 -5
- package/dist/services/optimistic.js +36 -10
- package/dist/services/vfs-client.js +11 -3
- package/dist/stores/agents.svelte.js +3 -2
- package/dist/stores/ai-persistence.svelte.js +7 -2
- package/dist/stores/ai.svelte.js +3 -2
- package/dist/stores/collaboration.svelte.d.ts +1 -1
- package/dist/stores/collaboration.svelte.js +3 -2
- package/dist/stores/editor.svelte.js +29 -5
- package/dist/stores/layout.svelte.js +3 -0
- package/dist/stores/plugin.svelte.js +9 -3
- package/dist/stores/vfs.svelte.js +26 -9
- package/dist/styles/theme.css +43 -0
- package/dist/types/vfs.d.ts +15 -1
- package/dist/types/vfs.js +9 -0
- package/dist/utils/language.d.ts +4 -3
- package/dist/utils/language.js +8 -18
- package/package.json +1 -1
- package/dist/components/editor/MinimalEditor.svelte +0 -75
- package/dist/components/editor/MinimalEditor.svelte.d.ts +0 -6
- package/dist/components/editor/MinimalEditor2.svelte +0 -84
- package/dist/components/editor/MinimalEditor2.svelte.d.ts +0 -6
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { getTokenizer } from '../tokenizer';
|
|
9
9
|
import { HISTORY_GROUP_TIMEOUT_MS, MAX_HISTORY_SIZE } from '../constants';
|
|
10
|
-
import { CursorManager, createCursorManager, getSelectionStart, getSelectionEnd, isSelectionEmpty } from './multi-cursor';
|
|
10
|
+
import { CursorManager, createCursorManager, comparePositions, getSelectionStart, getSelectionEnd, isSelectionEmpty } from './multi-cursor';
|
|
11
11
|
/**
|
|
12
12
|
* Core editor state class
|
|
13
13
|
*/
|
|
@@ -361,10 +361,12 @@ export class EditorState {
|
|
|
361
361
|
const start = getSelectionStart(cursor.selection);
|
|
362
362
|
const end = getSelectionEnd(cursor.selection);
|
|
363
363
|
this.deleteRangeInternal(start, end);
|
|
364
|
+
this.transformCursorUpdatesForDelete(updates, start, end);
|
|
364
365
|
currentPos = start;
|
|
365
366
|
}
|
|
366
367
|
// Insert text
|
|
367
368
|
const newPos = this.insertAtInternal(currentPos, text);
|
|
369
|
+
this.transformCursorUpdatesForInsert(updates, currentPos, text);
|
|
368
370
|
updates.push({ id: cursor.id, position: newPos });
|
|
369
371
|
}
|
|
370
372
|
// Apply all cursor updates using batch update to avoid multiple merge/emit calls
|
|
@@ -453,6 +455,7 @@ export class EditorState {
|
|
|
453
455
|
const start = getSelectionStart(cursor.selection);
|
|
454
456
|
const end = getSelectionEnd(cursor.selection);
|
|
455
457
|
this.deleteRangeInternal(start, end);
|
|
458
|
+
this.transformCursorUpdatesForDelete(updates, start, end);
|
|
456
459
|
updates.push({ id: cursor.id, position: start });
|
|
457
460
|
}
|
|
458
461
|
}
|
|
@@ -519,25 +522,32 @@ export class EditorState {
|
|
|
519
522
|
for (const cursor of cursors) {
|
|
520
523
|
const { line, column } = cursor.selection.head;
|
|
521
524
|
if (column > 0) {
|
|
525
|
+
const from = { line, column: column - 1 };
|
|
526
|
+
const to = { line, column };
|
|
522
527
|
// Delete within line
|
|
523
528
|
const currentLine = this._lines[line];
|
|
524
|
-
currentLine.text =
|
|
529
|
+
currentLine.text =
|
|
530
|
+
currentLine.text.slice(0, from.column) + currentLine.text.slice(to.column);
|
|
525
531
|
// Re-tokenize to end of document so deleting a multi-line construct
|
|
526
532
|
// delimiter re-propagates state to lines below.
|
|
527
533
|
this.retokenize(line, this._lines.length);
|
|
528
|
-
|
|
534
|
+
this.transformCursorUpdatesForDelete(updates, from, to);
|
|
535
|
+
updates.push({ id: cursor.id, position: from });
|
|
529
536
|
}
|
|
530
537
|
else if (line > 0) {
|
|
531
538
|
// Join with previous line
|
|
532
539
|
const prevLine = this._lines[line - 1];
|
|
533
540
|
const currentLine = this._lines[line];
|
|
534
541
|
const newColumn = prevLine.text.length;
|
|
542
|
+
const from = { line: line - 1, column: newColumn };
|
|
543
|
+
const to = { line, column: 0 };
|
|
535
544
|
prevLine.text += currentLine.text;
|
|
536
545
|
this._lines.splice(line, 1);
|
|
537
546
|
this._tokenizerStates.splice(line, 1);
|
|
538
547
|
this.renumberLines(line);
|
|
539
548
|
this.retokenize(line - 1, this._lines.length);
|
|
540
|
-
|
|
549
|
+
this.transformCursorUpdatesForDelete(updates, from, to);
|
|
550
|
+
updates.push({ id: cursor.id, position: from });
|
|
541
551
|
}
|
|
542
552
|
}
|
|
543
553
|
// Apply all cursor updates using batch update
|
|
@@ -564,28 +574,41 @@ export class EditorState {
|
|
|
564
574
|
return;
|
|
565
575
|
}
|
|
566
576
|
this.saveHistory('delete');
|
|
577
|
+
const updates = [];
|
|
567
578
|
for (const cursor of cursors) {
|
|
568
579
|
const { line, column } = cursor.selection.head;
|
|
569
580
|
const currentLine = this._lines[line];
|
|
570
581
|
if (column < currentLine.text.length) {
|
|
582
|
+
const from = { line, column };
|
|
583
|
+
const to = { line, column: column + 1 };
|
|
571
584
|
// Delete within line
|
|
572
|
-
currentLine.text =
|
|
585
|
+
currentLine.text =
|
|
586
|
+
currentLine.text.slice(0, from.column) + currentLine.text.slice(to.column);
|
|
573
587
|
// Re-tokenize to end of document so deleting a multi-line construct
|
|
574
588
|
// delimiter re-propagates state to lines below.
|
|
575
589
|
this.retokenize(line, this._lines.length);
|
|
590
|
+
this.transformCursorUpdatesForDelete(updates, from, to);
|
|
591
|
+
updates.push({ id: cursor.id, position: from });
|
|
576
592
|
}
|
|
577
593
|
else if (line < this._lines.length - 1) {
|
|
578
594
|
// Join with next line
|
|
579
595
|
const nextLine = this._lines[line + 1];
|
|
596
|
+
const from = { line, column };
|
|
597
|
+
const to = { line: line + 1, column: 0 };
|
|
580
598
|
currentLine.text += nextLine.text;
|
|
581
599
|
this._lines.splice(line + 1, 1);
|
|
582
600
|
this._tokenizerStates.splice(line + 1, 1);
|
|
583
601
|
this.renumberLines(line + 1);
|
|
584
602
|
this.retokenize(line, this._lines.length);
|
|
603
|
+
this.transformCursorUpdatesForDelete(updates, from, to);
|
|
604
|
+
updates.push({ id: cursor.id, position: from });
|
|
585
605
|
}
|
|
586
606
|
}
|
|
607
|
+
// Apply all cursor updates using batch update
|
|
608
|
+
this._cursorManager.batchUpdateCursors(updates);
|
|
587
609
|
this.emitChange({ type: 'delete', from: this.cursor, to: this.cursor });
|
|
588
610
|
this.emitSelectionChange();
|
|
611
|
+
this.emitCursorChange();
|
|
589
612
|
}
|
|
590
613
|
/**
|
|
591
614
|
* Insert a new line (enter key)
|
|
@@ -624,8 +647,8 @@ export class EditorState {
|
|
|
624
647
|
timestamp: now
|
|
625
648
|
};
|
|
626
649
|
if (shouldMerge) {
|
|
627
|
-
//
|
|
628
|
-
this._undoStack[this._undoStack.length - 1] =
|
|
650
|
+
// Keep the original restore snapshot; only extend the group's recency.
|
|
651
|
+
this._undoStack[this._undoStack.length - 1].timestamp = now;
|
|
629
652
|
}
|
|
630
653
|
else {
|
|
631
654
|
this._undoStack.push(entry);
|
|
@@ -715,6 +738,57 @@ export class EditorState {
|
|
|
715
738
|
head: { line: sel.head.line, column: sel.head.column }
|
|
716
739
|
};
|
|
717
740
|
}
|
|
741
|
+
transformCursorUpdatesForInsert(updates, at, text) {
|
|
742
|
+
for (const update of updates) {
|
|
743
|
+
update.position = this.transformPositionForInsert(update.position, at, text);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
transformPositionForInsert(position, at, text) {
|
|
747
|
+
if (comparePositions(position, at) < 0) {
|
|
748
|
+
return position;
|
|
749
|
+
}
|
|
750
|
+
const textLines = text.split('\n');
|
|
751
|
+
const insertedLineCount = textLines.length - 1;
|
|
752
|
+
if (insertedLineCount === 0) {
|
|
753
|
+
if (position.line !== at.line) {
|
|
754
|
+
return position;
|
|
755
|
+
}
|
|
756
|
+
return { line: position.line, column: position.column + text.length };
|
|
757
|
+
}
|
|
758
|
+
if (position.line === at.line) {
|
|
759
|
+
return {
|
|
760
|
+
line: position.line + insertedLineCount,
|
|
761
|
+
column: textLines[textLines.length - 1].length + position.column - at.column
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
return { line: position.line + insertedLineCount, column: position.column };
|
|
765
|
+
}
|
|
766
|
+
transformCursorUpdatesForDelete(updates, from, to) {
|
|
767
|
+
for (const update of updates) {
|
|
768
|
+
update.position = this.transformPositionForDelete(update.position, from, to);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
transformPositionForDelete(position, from, to) {
|
|
772
|
+
if (comparePositions(position, from) <= 0) {
|
|
773
|
+
return position;
|
|
774
|
+
}
|
|
775
|
+
if (comparePositions(position, to) <= 0) {
|
|
776
|
+
return from;
|
|
777
|
+
}
|
|
778
|
+
if (from.line === to.line) {
|
|
779
|
+
if (position.line !== from.line) {
|
|
780
|
+
return position;
|
|
781
|
+
}
|
|
782
|
+
return { line: position.line, column: position.column - (to.column - from.column) };
|
|
783
|
+
}
|
|
784
|
+
if (position.line === to.line) {
|
|
785
|
+
return {
|
|
786
|
+
line: from.line,
|
|
787
|
+
column: from.column + position.column - to.column
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
return { line: position.line - (to.line - from.line), column: position.column };
|
|
791
|
+
}
|
|
718
792
|
/**
|
|
719
793
|
* Check if undo is available
|
|
720
794
|
*/
|
|
@@ -738,11 +812,56 @@ export class EditorState {
|
|
|
738
812
|
return true;
|
|
739
813
|
if (!a || !b)
|
|
740
814
|
return a === b;
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
815
|
+
const aKeys = Object.keys(a)
|
|
816
|
+
.filter((key) => a[key] !== undefined)
|
|
817
|
+
.sort();
|
|
818
|
+
const bKeys = Object.keys(b)
|
|
819
|
+
.filter((key) => b[key] !== undefined)
|
|
820
|
+
.sort();
|
|
821
|
+
if (aKeys.length !== bKeys.length)
|
|
822
|
+
return false;
|
|
823
|
+
for (let i = 0; i < aKeys.length; i++) {
|
|
824
|
+
const key = aKeys[i];
|
|
825
|
+
if (key !== bKeys[i])
|
|
826
|
+
return false;
|
|
827
|
+
if (!this.tokenizerStateValuesEqual(a[key], b[key])) {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
833
|
+
tokenizerStateValuesEqual(a, b) {
|
|
834
|
+
if (a === b)
|
|
835
|
+
return true;
|
|
836
|
+
if (typeof a !== typeof b)
|
|
837
|
+
return false;
|
|
838
|
+
if (a === null || b === null)
|
|
839
|
+
return a === b;
|
|
840
|
+
if (typeof a !== 'object' || typeof b !== 'object')
|
|
841
|
+
return false;
|
|
842
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
843
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
|
|
844
|
+
return false;
|
|
845
|
+
return a.every((value, index) => this.tokenizerStateValuesEqual(value, b[index]));
|
|
846
|
+
}
|
|
847
|
+
const aRecord = a;
|
|
848
|
+
const bRecord = b;
|
|
849
|
+
const aKeys = Object.keys(aRecord)
|
|
850
|
+
.filter((key) => aRecord[key] !== undefined)
|
|
851
|
+
.sort();
|
|
852
|
+
const bKeys = Object.keys(bRecord)
|
|
853
|
+
.filter((key) => bRecord[key] !== undefined)
|
|
854
|
+
.sort();
|
|
855
|
+
if (aKeys.length !== bKeys.length)
|
|
856
|
+
return false;
|
|
857
|
+
for (let i = 0; i < aKeys.length; i++) {
|
|
858
|
+
const key = aKeys[i];
|
|
859
|
+
if (key !== bKeys[i])
|
|
860
|
+
return false;
|
|
861
|
+
if (!this.tokenizerStateValuesEqual(aRecord[key], bRecord[key]))
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
return true;
|
|
746
865
|
}
|
|
747
866
|
/**
|
|
748
867
|
* Re-tokenize a range of lines with early-exit optimization
|
|
@@ -92,19 +92,20 @@ export function createEditorFind(deps) {
|
|
|
92
92
|
return [];
|
|
93
93
|
const { scrollTop, viewportHeight } = viewport;
|
|
94
94
|
const { lineHeight, charWidth, gutterWidth, contentPadding } = measurements;
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
95
|
+
const lineToVisualRow = deps.lineToVisualRow ?? ((line) => line);
|
|
96
|
+
const firstVisibleRow = Math.max(0, Math.floor(scrollTop / lineHeight) - 1);
|
|
97
|
+
const lastVisibleRow = Math.max(0, Math.ceil((scrollTop + viewportHeight) / lineHeight) + 1);
|
|
98
98
|
const rects = [];
|
|
99
99
|
for (let i = 0; i < matches.length; i++) {
|
|
100
100
|
const match = matches[i];
|
|
101
|
-
|
|
101
|
+
const visualRow = lineToVisualRow(match.line);
|
|
102
|
+
if (visualRow < firstVisibleRow || visualRow > lastVisibleRow)
|
|
102
103
|
continue;
|
|
103
104
|
const width = (match.endColumn - match.startColumn) * charWidth;
|
|
104
105
|
if (width <= 0)
|
|
105
106
|
continue;
|
|
106
107
|
rects.push({
|
|
107
|
-
top:
|
|
108
|
+
top: visualRow * lineHeight,
|
|
108
109
|
left: gutterWidth + contentPadding + match.startColumn * charWidth,
|
|
109
110
|
width,
|
|
110
111
|
height: lineHeight,
|
|
@@ -30,6 +30,7 @@ export interface EditorInputDeps {
|
|
|
30
30
|
lineHeight: number;
|
|
31
31
|
gutterWidth: number;
|
|
32
32
|
};
|
|
33
|
+
visualRowToLine?: (visualRow: number) => number;
|
|
33
34
|
/**
|
|
34
35
|
* Editor tab size (columns per tab stop). Optional for backwards
|
|
35
36
|
* compatibility; when omitted, DEFAULT_TAB_SIZE is used. Needed so that
|
|
@@ -82,7 +82,10 @@ export function createEditorInput(deps) {
|
|
|
82
82
|
const x = e.clientX - rect.left - gutterWidth - CONTENT_PADDING;
|
|
83
83
|
const y = e.clientY - rect.top;
|
|
84
84
|
const editorState = deps.getEditorState();
|
|
85
|
-
const
|
|
85
|
+
const visualRow = Math.max(0, Math.min(Math.floor((y + scrollTop) / lineHeight), editorState.lineCount - 1));
|
|
86
|
+
const line = deps.visualRowToLine
|
|
87
|
+
? deps.visualRowToLine(visualRow)
|
|
88
|
+
: Math.max(0, Math.min(visualRow, editorState.lineCount - 1));
|
|
86
89
|
const lineContent = editorState.getLine(line);
|
|
87
90
|
const lineText = lineContent?.text ?? '';
|
|
88
91
|
const tabSize = deps.getTabSize?.() ?? DEFAULT_TAB_SIZE;
|
|
@@ -2,6 +2,7 @@ import type { Selection } from './core';
|
|
|
2
2
|
export interface ScrollConfig {
|
|
3
3
|
getEditorContent: () => HTMLDivElement | null;
|
|
4
4
|
getSelection: () => Selection;
|
|
5
|
+
lineToVisualRow?: (line: number) => number;
|
|
5
6
|
getMeasurements: () => {
|
|
6
7
|
lineHeight: number;
|
|
7
8
|
charWidth: number;
|
|
@@ -8,7 +8,8 @@ export function createEditorScroll(config) {
|
|
|
8
8
|
return;
|
|
9
9
|
const selection = config.getSelection();
|
|
10
10
|
const { lineHeight, charWidth, gutterWidth } = config.getMeasurements();
|
|
11
|
-
const
|
|
11
|
+
const lineToVisualRow = config.lineToVisualRow ?? ((line) => line);
|
|
12
|
+
const cursorTop = lineToVisualRow(selection.head.line) * lineHeight;
|
|
12
13
|
const cursorLeft = gutterWidth + CONTENT_PADDING + selection.head.column * charWidth;
|
|
13
14
|
const viewportHeight = editorContent.clientHeight;
|
|
14
15
|
const viewportWidth = editorContent.clientWidth;
|
|
@@ -3,13 +3,29 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { default as Editor } from './Editor.svelte';
|
|
5
5
|
export { default as CustomEditor } from './CustomEditor.svelte';
|
|
6
|
-
export { default as CollaborativeEditor } from './CollaborativeEditor.svelte';
|
|
7
6
|
export { default as EditorTabs } from './EditorTabs.svelte';
|
|
8
7
|
export { default as EditorPane } from './EditorPane.svelte';
|
|
9
8
|
export { default as FileIcon } from './FileIcon.svelte';
|
|
10
9
|
export { default as FileExplorer } from './FileExplorer.svelte';
|
|
11
|
-
export * from './core';
|
|
10
|
+
export * from './core/state';
|
|
11
|
+
export * from './core/navigation';
|
|
12
|
+
export * from './core/keybindings';
|
|
13
|
+
export * from './core/search';
|
|
14
|
+
export * from './core/folding';
|
|
15
|
+
export * from './core/multi-cursor';
|
|
16
|
+
export * from './core/complexity-analyzer';
|
|
17
|
+
export * from './core/ai-awareness';
|
|
18
|
+
export * from './core/semantic-analyzer';
|
|
19
|
+
export * from './core/commands';
|
|
20
|
+
export * from './core/bracket-healer';
|
|
21
|
+
export * from './core/git-blame';
|
|
22
|
+
export * from './core/snippet-manager';
|
|
23
|
+
export * from './core/quick-actions';
|
|
24
|
+
export * from './core/diagnostics';
|
|
25
|
+
export * from './core/breakpoints';
|
|
26
|
+
export type { Position } from './core/state';
|
|
27
|
+
export type { Diagnostic, Range } from './core/quick-actions';
|
|
12
28
|
export * from './theme';
|
|
13
29
|
export { getLanguageExtension, getLanguageConfig, getLanguageFromExtension, getLanguageFromFilename, getLanguageFromMimeType, getAllLanguageConfigs, resolveLanguage, type LanguageConfig } from './languages';
|
|
14
30
|
export { getSupportedLanguages, isLanguageSupported } from './languages';
|
|
15
|
-
export { getTokenizer, tokenize, getTokenClass, tokensToHTML, PlaintextTokenizer,
|
|
31
|
+
export { getTokenizer, tokenize, getTokenClass, tokensToHTML, PlaintextTokenizer, createToken, type Token, type TokenizedLine, type TokenizerState, type TokenType, type LanguageTokenizer } from './tokenizer';
|
|
@@ -4,13 +4,27 @@
|
|
|
4
4
|
// Components
|
|
5
5
|
export { default as Editor } from './Editor.svelte';
|
|
6
6
|
export { default as CustomEditor } from './CustomEditor.svelte';
|
|
7
|
-
export { default as CollaborativeEditor } from './CollaborativeEditor.svelte';
|
|
8
7
|
export { default as EditorTabs } from './EditorTabs.svelte';
|
|
9
8
|
export { default as EditorPane } from './EditorPane.svelte';
|
|
10
9
|
export { default as FileIcon } from './FileIcon.svelte';
|
|
11
10
|
export { default as FileExplorer } from './FileExplorer.svelte';
|
|
12
|
-
// Core utilities
|
|
13
|
-
export * from './core';
|
|
11
|
+
// Core utilities (explicitly excluding CRDT binding; use @nocturnium/svelte-ide/crdt)
|
|
12
|
+
export * from './core/state';
|
|
13
|
+
export * from './core/navigation';
|
|
14
|
+
export * from './core/keybindings';
|
|
15
|
+
export * from './core/search';
|
|
16
|
+
export * from './core/folding';
|
|
17
|
+
export * from './core/multi-cursor';
|
|
18
|
+
export * from './core/complexity-analyzer';
|
|
19
|
+
export * from './core/ai-awareness';
|
|
20
|
+
export * from './core/semantic-analyzer';
|
|
21
|
+
export * from './core/commands';
|
|
22
|
+
export * from './core/bracket-healer';
|
|
23
|
+
export * from './core/git-blame';
|
|
24
|
+
export * from './core/snippet-manager';
|
|
25
|
+
export * from './core/quick-actions';
|
|
26
|
+
export * from './core/diagnostics';
|
|
27
|
+
export * from './core/breakpoints';
|
|
14
28
|
// Theme
|
|
15
29
|
export * from './theme';
|
|
16
30
|
// Languages (explicit exports to avoid conflicts with tokenizer)
|
|
@@ -18,4 +32,4 @@ export { getLanguageExtension, getLanguageConfig, getLanguageFromExtension, getL
|
|
|
18
32
|
// Re-export from languages (these exist in both but we prefer languages versions)
|
|
19
33
|
export { getSupportedLanguages, isLanguageSupported } from './languages';
|
|
20
34
|
// Tokenizer (explicit exports to avoid conflicts)
|
|
21
|
-
export { getTokenizer, tokenize, getTokenClass, tokensToHTML, PlaintextTokenizer,
|
|
35
|
+
export { getTokenizer, tokenize, getTokenClass, tokensToHTML, PlaintextTokenizer, createToken } from './tokenizer';
|
|
@@ -1,35 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Base tokenizer with common functionality
|
|
3
3
|
*/
|
|
4
|
-
import type { Token, TokenizedLine, TokenizerState, TokenType,
|
|
4
|
+
import type { Token, TokenizedLine, TokenizerState, TokenType, LanguageTokenizer } from './types';
|
|
5
5
|
/**
|
|
6
6
|
* Create a token
|
|
7
7
|
*/
|
|
8
8
|
export declare function createToken(type: TokenType, text: string, start: number): Token;
|
|
9
|
-
/**
|
|
10
|
-
* Base tokenizer class using grammar rules
|
|
11
|
-
*/
|
|
12
|
-
export declare class GrammarTokenizer implements LanguageTokenizer {
|
|
13
|
-
language: string;
|
|
14
|
-
private grammar;
|
|
15
|
-
constructor(grammar: LanguageGrammar);
|
|
16
|
-
getInitialState(): TokenizerState;
|
|
17
|
-
tokenizeLine(line: string, lineNumber: number, prevState?: TokenizerState): TokenizedLine;
|
|
18
|
-
private updateState;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Simple regex-based tokenizer for basic languages
|
|
22
|
-
*/
|
|
23
|
-
export declare class SimpleTokenizer implements LanguageTokenizer {
|
|
24
|
-
language: string;
|
|
25
|
-
private patterns;
|
|
26
|
-
constructor(language: string, patterns: Array<{
|
|
27
|
-
type: TokenType;
|
|
28
|
-
regex: RegExp;
|
|
29
|
-
}>);
|
|
30
|
-
getInitialState(): TokenizerState;
|
|
31
|
-
tokenizeLine(line: string, lineNumber: number, prevState?: TokenizerState): TokenizedLine;
|
|
32
|
-
}
|
|
33
9
|
/**
|
|
34
10
|
* Plaintext tokenizer - no highlighting
|
|
35
11
|
*/
|
|
@@ -12,178 +12,6 @@ export function createToken(type, text, start) {
|
|
|
12
12
|
end: start + text.length
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Base tokenizer class using grammar rules
|
|
17
|
-
*/
|
|
18
|
-
export class GrammarTokenizer {
|
|
19
|
-
language;
|
|
20
|
-
grammar;
|
|
21
|
-
constructor(grammar) {
|
|
22
|
-
this.language = grammar.language;
|
|
23
|
-
this.grammar = grammar;
|
|
24
|
-
}
|
|
25
|
-
getInitialState() {
|
|
26
|
-
return {};
|
|
27
|
-
}
|
|
28
|
-
tokenizeLine(line, lineNumber, prevState) {
|
|
29
|
-
const tokens = [];
|
|
30
|
-
let pos = 0;
|
|
31
|
-
const state = { ...prevState };
|
|
32
|
-
let currentRuleSet = 'root';
|
|
33
|
-
// Determine starting rule set based on state
|
|
34
|
-
if (state.inBlockComment) {
|
|
35
|
-
currentRuleSet = 'blockComment';
|
|
36
|
-
}
|
|
37
|
-
else if (state.inTemplateLiteral) {
|
|
38
|
-
currentRuleSet = 'templateLiteral';
|
|
39
|
-
}
|
|
40
|
-
else if (state.inMultilineString) {
|
|
41
|
-
currentRuleSet = 'multilineString';
|
|
42
|
-
}
|
|
43
|
-
while (pos < line.length) {
|
|
44
|
-
const remaining = line.slice(pos);
|
|
45
|
-
let matched = false;
|
|
46
|
-
// Try to match rules in current rule set
|
|
47
|
-
const rules = this.grammar.rules[currentRuleSet] || this.grammar.rules['root'] || [];
|
|
48
|
-
for (const rule of rules) {
|
|
49
|
-
const match = remaining.match(rule.pattern);
|
|
50
|
-
if (match && match.index === 0) {
|
|
51
|
-
const text = match[0];
|
|
52
|
-
tokens.push(createToken(rule.type, text, pos));
|
|
53
|
-
pos += text.length;
|
|
54
|
-
matched = true;
|
|
55
|
-
// Handle state transitions
|
|
56
|
-
if (rule.nextState) {
|
|
57
|
-
currentRuleSet = rule.nextState;
|
|
58
|
-
this.updateState(state, rule.nextState, true);
|
|
59
|
-
}
|
|
60
|
-
if (rule.popState) {
|
|
61
|
-
currentRuleSet = 'root';
|
|
62
|
-
this.updateState(state, currentRuleSet, false);
|
|
63
|
-
}
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// No match - consume single character as text
|
|
68
|
-
if (!matched) {
|
|
69
|
-
const char = line[pos];
|
|
70
|
-
// Try to merge with previous text token
|
|
71
|
-
const lastToken = tokens[tokens.length - 1];
|
|
72
|
-
if (lastToken && lastToken.type === 'text' && lastToken.end === pos) {
|
|
73
|
-
lastToken.text += char;
|
|
74
|
-
lastToken.end += 1;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
tokens.push(createToken('text', char, pos));
|
|
78
|
-
}
|
|
79
|
-
pos += 1;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// Handle empty lines
|
|
83
|
-
if (tokens.length === 0) {
|
|
84
|
-
tokens.push(createToken('text', '', 0));
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
lineNumber,
|
|
88
|
-
tokens,
|
|
89
|
-
text: line,
|
|
90
|
-
state: { ...state }
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
updateState(state, ruleSet, entering) {
|
|
94
|
-
switch (ruleSet) {
|
|
95
|
-
case 'blockComment':
|
|
96
|
-
state.inBlockComment = entering;
|
|
97
|
-
break;
|
|
98
|
-
case 'templateLiteral':
|
|
99
|
-
state.inTemplateLiteral = entering;
|
|
100
|
-
break;
|
|
101
|
-
case 'multilineString':
|
|
102
|
-
state.inMultilineString = entering;
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Simple regex-based tokenizer for basic languages
|
|
109
|
-
*/
|
|
110
|
-
export class SimpleTokenizer {
|
|
111
|
-
language;
|
|
112
|
-
patterns;
|
|
113
|
-
constructor(language, patterns) {
|
|
114
|
-
this.language = language;
|
|
115
|
-
this.patterns = patterns;
|
|
116
|
-
}
|
|
117
|
-
getInitialState() {
|
|
118
|
-
return {};
|
|
119
|
-
}
|
|
120
|
-
tokenizeLine(line, lineNumber, prevState) {
|
|
121
|
-
const tokens = [];
|
|
122
|
-
let pos = 0;
|
|
123
|
-
const state = { ...prevState };
|
|
124
|
-
// Handle block comment continuation
|
|
125
|
-
if (state.inBlockComment) {
|
|
126
|
-
const endMatch = line.indexOf('*/');
|
|
127
|
-
if (endMatch !== -1) {
|
|
128
|
-
tokens.push(createToken('comment.block', line.slice(0, endMatch + 2), 0));
|
|
129
|
-
pos = endMatch + 2;
|
|
130
|
-
state.inBlockComment = false;
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
tokens.push(createToken('comment.block', line, 0));
|
|
134
|
-
return { lineNumber, tokens, text: line, state };
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
while (pos < line.length) {
|
|
138
|
-
const remaining = line.slice(pos);
|
|
139
|
-
let matched = false;
|
|
140
|
-
// Check for block comment start
|
|
141
|
-
if (remaining.startsWith('/*')) {
|
|
142
|
-
const endMatch = remaining.indexOf('*/', 2);
|
|
143
|
-
if (endMatch !== -1) {
|
|
144
|
-
const text = remaining.slice(0, endMatch + 2);
|
|
145
|
-
tokens.push(createToken('comment.block', text, pos));
|
|
146
|
-
pos += text.length;
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
tokens.push(createToken('comment.block', remaining, pos));
|
|
150
|
-
state.inBlockComment = true;
|
|
151
|
-
pos = line.length;
|
|
152
|
-
}
|
|
153
|
-
matched = true;
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
// Try each pattern
|
|
157
|
-
for (const { type, regex } of this.patterns) {
|
|
158
|
-
const match = remaining.match(regex);
|
|
159
|
-
if (match && match.index === 0) {
|
|
160
|
-
const text = match[0];
|
|
161
|
-
tokens.push(createToken(type, text, pos));
|
|
162
|
-
pos += text.length;
|
|
163
|
-
matched = true;
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// No match - consume character
|
|
168
|
-
if (!matched) {
|
|
169
|
-
const char = remaining[0];
|
|
170
|
-
const lastToken = tokens[tokens.length - 1];
|
|
171
|
-
if (lastToken && lastToken.type === 'text' && lastToken.end === pos) {
|
|
172
|
-
lastToken.text += char;
|
|
173
|
-
lastToken.end += 1;
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
tokens.push(createToken('text', char, pos));
|
|
177
|
-
}
|
|
178
|
-
pos += 1;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (tokens.length === 0) {
|
|
182
|
-
tokens.push(createToken('text', '', 0));
|
|
183
|
-
}
|
|
184
|
-
return { lineNumber, tokens, text: line, state };
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
15
|
/**
|
|
188
16
|
* Plaintext tokenizer - no highlighting
|
|
189
17
|
*/
|
|
@@ -15,6 +15,10 @@ export { GoTokenizer, createGoTokenizer } from './languages/go';
|
|
|
15
15
|
export { MarkdownTokenizer, createMarkdownTokenizer } from './languages/markdown';
|
|
16
16
|
export { SvelteTokenizer, createSvelteTokenizer } from './languages/svelte';
|
|
17
17
|
import type { LanguageTokenizer, Token, TokenizedLine, TokenizerState, TokenType } from './types';
|
|
18
|
+
/**
|
|
19
|
+
* Tokenizer factory functions by language
|
|
20
|
+
*/
|
|
21
|
+
export declare const tokenizerFactories: Record<string, () => LanguageTokenizer>;
|
|
18
22
|
/**
|
|
19
23
|
* Get the canonical language name from an alias or extension
|
|
20
24
|
*/
|
|
@@ -78,7 +78,7 @@ const languageAliases = {
|
|
|
78
78
|
/**
|
|
79
79
|
* Tokenizer factory functions by language
|
|
80
80
|
*/
|
|
81
|
-
const tokenizerFactories = {
|
|
81
|
+
export const tokenizerFactories = {
|
|
82
82
|
javascript: createJavaScriptTokenizer,
|
|
83
83
|
typescript: createTypeScriptTokenizer,
|
|
84
84
|
jsx: createJSXTokenizer,
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { TokenizedLine, TokenizerState } from '../types';
|
|
5
5
|
interface HTMLTokenizerState extends TokenizerState {
|
|
6
|
-
inTag?: boolean;
|
|
7
6
|
inScript?: boolean;
|
|
8
7
|
inStyle?: boolean;
|
|
9
|
-
|
|
8
|
+
innerState?: TokenizerState;
|
|
10
9
|
}
|
|
11
10
|
export declare class HTMLTokenizer {
|
|
12
11
|
language: string;
|
|
13
12
|
private isXML;
|
|
13
|
+
private jsTokenizer;
|
|
14
|
+
private cssTokenizer;
|
|
14
15
|
constructor(options?: {
|
|
15
16
|
xml?: boolean;
|
|
16
17
|
});
|