@nocturnium/svelte-ide 1.0.6 → 1.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/dist/components/editor/ComplexityLayer.svelte +22 -3
- package/dist/components/editor/ComplexityLayer.svelte.d.ts +7 -0
- package/dist/components/editor/CustomEditor.svelte +17 -4
- package/dist/components/editor/core/folding.d.ts +3 -4
- package/dist/components/editor/core/folding.js +36 -0
- package/package.json +1 -1
|
@@ -17,13 +17,27 @@
|
|
|
17
17
|
lineHeight: number;
|
|
18
18
|
/** Gutter width in pixels */
|
|
19
19
|
gutterWidth?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Full height of the scrollable content in pixels. The layer must span the
|
|
22
|
+
* whole document (not just one viewport) or indicators below the first
|
|
23
|
+
* screenful get clipped — which previously hid every high-complexity region
|
|
24
|
+
* that sat past the initial view.
|
|
25
|
+
*/
|
|
26
|
+
totalHeight?: number;
|
|
20
27
|
/** Minimum score to show indicators (default: 50) */
|
|
21
28
|
minScore?: number;
|
|
22
29
|
/** Whether indicators are enabled */
|
|
23
30
|
enabled?: boolean;
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
let {
|
|
33
|
+
let {
|
|
34
|
+
metrics,
|
|
35
|
+
lineHeight,
|
|
36
|
+
gutterWidth = 50,
|
|
37
|
+
totalHeight = 0,
|
|
38
|
+
minScore = 50,
|
|
39
|
+
enabled = true
|
|
40
|
+
}: Props = $props();
|
|
27
41
|
|
|
28
42
|
// Filter regions that exceed the threshold
|
|
29
43
|
let highlightedRegions = $derived(
|
|
@@ -116,7 +130,11 @@
|
|
|
116
130
|
</script>
|
|
117
131
|
|
|
118
132
|
{#if enabled && lineScores.size > 0}
|
|
119
|
-
<div
|
|
133
|
+
<div
|
|
134
|
+
class="complexity-gutter"
|
|
135
|
+
aria-hidden="true"
|
|
136
|
+
style="width: {gutterWidth}px;{totalHeight ? ` height: ${totalHeight}px;` : ''}"
|
|
137
|
+
>
|
|
120
138
|
{#each [...lineScores.entries()] as [line, data] (line)}
|
|
121
139
|
<div
|
|
122
140
|
class="complexity-gutter__indicator"
|
|
@@ -176,10 +194,11 @@
|
|
|
176
194
|
position: absolute;
|
|
177
195
|
top: 0;
|
|
178
196
|
left: 0;
|
|
197
|
+
/* Height is set inline to the full content height so indicators below the
|
|
198
|
+
first viewport are not clipped. Fall back to the viewport when unknown. */
|
|
179
199
|
bottom: 0;
|
|
180
200
|
pointer-events: none;
|
|
181
201
|
z-index: 5;
|
|
182
|
-
overflow: hidden;
|
|
183
202
|
}
|
|
184
203
|
|
|
185
204
|
.complexity-gutter__indicator {
|
|
@@ -6,6 +6,13 @@ interface Props {
|
|
|
6
6
|
lineHeight: number;
|
|
7
7
|
/** Gutter width in pixels */
|
|
8
8
|
gutterWidth?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Full height of the scrollable content in pixels. The layer must span the
|
|
11
|
+
* whole document (not just one viewport) or indicators below the first
|
|
12
|
+
* screenful get clipped — which previously hid every high-complexity region
|
|
13
|
+
* that sat past the initial view.
|
|
14
|
+
*/
|
|
15
|
+
totalHeight?: number;
|
|
9
16
|
/** Minimum score to show indicators (default: 50) */
|
|
10
17
|
minScore?: number;
|
|
11
18
|
/** Whether indicators are enabled */
|
|
@@ -510,10 +510,22 @@
|
|
|
510
510
|
if (!folding || !editorState) return;
|
|
511
511
|
foldManager.expandAll();
|
|
512
512
|
const analyzer = getSemanticAnalyzer();
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
513
|
+
const semanticRegions = analyzer.analyze(editorState.lines, language);
|
|
514
|
+
const foldRegions = foldManager.getRegions();
|
|
515
|
+
|
|
516
|
+
// Semantic region boundaries don't always line up exactly with bracket-fold
|
|
517
|
+
// headers — a function's foldable `{` can sit a line below its doc-commented
|
|
518
|
+
// header, and a hidden category (e.g. `private`, `error-handling`) may cover
|
|
519
|
+
// several nested blocks. So collapse every foldable block whose header falls
|
|
520
|
+
// within a hidden semantic region's range, rather than requiring an exact
|
|
521
|
+
// header-line match (which silently folded almost nothing). collapse() is
|
|
522
|
+
// idempotent, so collapsing the same header twice is harmless.
|
|
523
|
+
for (const region of semanticRegions) {
|
|
524
|
+
if (!preset.hide.includes(region.category)) continue;
|
|
525
|
+
for (const fold of foldRegions) {
|
|
526
|
+
if (fold.startLine >= region.startLine && fold.startLine <= region.endLine) {
|
|
527
|
+
foldManager.collapse(fold.startLine);
|
|
528
|
+
}
|
|
517
529
|
}
|
|
518
530
|
}
|
|
519
531
|
}
|
|
@@ -993,6 +1005,7 @@
|
|
|
993
1005
|
metrics={complexityMetrics}
|
|
994
1006
|
{lineHeight}
|
|
995
1007
|
{gutterWidth}
|
|
1008
|
+
totalHeight={totalContentHeight}
|
|
996
1009
|
minScore={complexityThreshold}
|
|
997
1010
|
enabled={complexityHighlighting}
|
|
998
1011
|
/>
|
|
@@ -18,7 +18,7 @@ export interface FoldRegion {
|
|
|
18
18
|
/** Indentation level (for nested folds) */
|
|
19
19
|
level: number;
|
|
20
20
|
/** Type of fold region */
|
|
21
|
-
type: 'bracket' | 'indentation' | 'region' | 'comment';
|
|
21
|
+
type: 'bracket' | 'indentation' | 'region' | 'comment' | 'import';
|
|
22
22
|
/** Whether this region is currently collapsed */
|
|
23
23
|
collapsed: boolean;
|
|
24
24
|
}
|
|
@@ -34,12 +34,11 @@ export interface FoldingConfig {
|
|
|
34
34
|
regions: boolean;
|
|
35
35
|
/** Enable multi-line comment folding */
|
|
36
36
|
comments: boolean;
|
|
37
|
+
/** Enable folding a run of consecutive import statements */
|
|
38
|
+
imports: boolean;
|
|
37
39
|
/** Minimum lines for a foldable region */
|
|
38
40
|
minLines: number;
|
|
39
41
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Detect all fold regions in a document
|
|
42
|
-
*/
|
|
43
42
|
export declare function detectFoldRegions(lines: readonly Line[], language?: string, config?: Partial<FoldingConfig>): FoldRegion[];
|
|
44
43
|
/**
|
|
45
44
|
* Fold state manager
|
|
@@ -11,6 +11,7 @@ const DEFAULT_CONFIG = {
|
|
|
11
11
|
indentation: true,
|
|
12
12
|
regions: true,
|
|
13
13
|
comments: true,
|
|
14
|
+
imports: true,
|
|
14
15
|
minLines: 2
|
|
15
16
|
};
|
|
16
17
|
/**
|
|
@@ -424,6 +425,38 @@ function detectCommentFolds(lines, minLines = 2) {
|
|
|
424
425
|
/**
|
|
425
426
|
* Detect all fold regions in a document
|
|
426
427
|
*/
|
|
428
|
+
/**
|
|
429
|
+
* Detect a fold region for each run of consecutive import statements. Import
|
|
430
|
+
* groups have no braces of their own, so without this an editor (and the
|
|
431
|
+
* "fold imports" semantic preset) can't collapse them.
|
|
432
|
+
*/
|
|
433
|
+
function detectImportFolds(lines, minLines = 2) {
|
|
434
|
+
const regions = [];
|
|
435
|
+
// ES `import ... from`, side-effect `import '...'`, and Python `from x import`.
|
|
436
|
+
const isImportLine = (text) => /^\s*import\b/.test(text) || /^\s*from\s+\S+\s+import\b/.test(text);
|
|
437
|
+
let start = -1;
|
|
438
|
+
for (let i = 0; i <= lines.length; i++) {
|
|
439
|
+
const isImport = i < lines.length && isImportLine(lines[i].text);
|
|
440
|
+
if (isImport) {
|
|
441
|
+
if (start === -1)
|
|
442
|
+
start = i;
|
|
443
|
+
}
|
|
444
|
+
else if (start !== -1) {
|
|
445
|
+
const end = i - 1;
|
|
446
|
+
if (end - start + 1 >= minLines) {
|
|
447
|
+
regions.push({
|
|
448
|
+
startLine: start,
|
|
449
|
+
endLine: end,
|
|
450
|
+
level: 0,
|
|
451
|
+
type: 'import',
|
|
452
|
+
collapsed: false
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
start = -1;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return regions;
|
|
459
|
+
}
|
|
427
460
|
export function detectFoldRegions(lines, language = 'plaintext', config = {}) {
|
|
428
461
|
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
429
462
|
const allRegions = [];
|
|
@@ -459,6 +492,9 @@ export function detectFoldRegions(lines, language = 'plaintext', config = {}) {
|
|
|
459
492
|
if (cfg.comments) {
|
|
460
493
|
allRegions.push(...detectCommentFolds(lines, cfg.minLines));
|
|
461
494
|
}
|
|
495
|
+
if (cfg.imports) {
|
|
496
|
+
allRegions.push(...detectImportFolds(lines, cfg.minLines));
|
|
497
|
+
}
|
|
462
498
|
// Sort by start line, then by length (longer regions first for nesting)
|
|
463
499
|
allRegions.sort((a, b) => {
|
|
464
500
|
if (a.startLine !== b.startLine) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocturnium/svelte-ide",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Svelte 5 code editor and IDE building blocks — custom editor, syntax highlighting, code folding, multi-cursor, LSP client, and optional realtime collaboration.",
|
|
5
5
|
"author": "Nocturnium & Jordan Dziat <hello@nocturnium.ai> (https://nocturnium.ai)",
|
|
6
6
|
"license": "MIT",
|