@codegraft/core 0.1.0-beta.1 → 0.1.0-beta.11
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 +3 -2
- package/dist/collection.d.ts +17 -12
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +90 -125
- package/dist/collection.js.map +1 -1
- package/dist/comment-attachment.d.ts +6 -7
- package/dist/comment-attachment.d.ts.map +1 -1
- package/dist/comment-attachment.js +8 -8
- package/dist/comment-attachment.js.map +1 -1
- package/dist/containers.d.ts +15 -0
- package/dist/containers.d.ts.map +1 -0
- package/dist/containers.js +39 -0
- package/dist/containers.js.map +1 -0
- package/dist/edit-collector.d.ts +6 -14
- package/dist/edit-collector.d.ts.map +1 -1
- package/dist/edit-collector.js +6 -34
- package/dist/edit-collector.js.map +1 -1
- package/dist/format.d.ts +41 -3
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +80 -7
- package/dist/format.js.map +1 -1
- package/dist/formatter.d.ts +73 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +225 -0
- package/dist/formatter.js.map +1 -0
- package/dist/generated/node-types.d.ts +9 -6
- package/dist/generated/node-types.d.ts.map +1 -1
- package/dist/generated/node-types.js +1 -1
- package/dist/generated/node-types.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -3
- package/dist/internal.js.map +1 -1
- package/dist/parser.d.ts +3 -6
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +36 -50
- package/dist/parser.js.map +1 -1
- package/dist/resolver.js +3 -6
- package/dist/resolver.js.map +1 -1
- package/dist/rich-node.d.ts.map +1 -1
- package/dist/rich-node.js +8 -9
- package/dist/rich-node.js.map +1 -1
- package/dist/types.d.ts +17 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -17
- package/wasm/tree-sitter-css.wasm +0 -0
- package/wasm/tree-sitter-html.wasm +0 -0
- package/wasm/tree-sitter-typescript.wasm +0 -0
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The node types
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Every grammar Codegraft targets today names its comment node `comment`; the map is
|
|
7
|
-
* kept per-grammar so a future grammar with a different name slots in without
|
|
8
|
-
* touching either consumer.
|
|
2
|
+
* The comment node types per grammar — the single source of truth behind {@link isComment}.
|
|
3
|
+
* Every grammar Codegraft targets names its comment node `comment`; keeping the map per-grammar lets
|
|
4
|
+
* a future grammar with a different name slot in without touching any consumer.
|
|
9
5
|
*/
|
|
10
6
|
export const COMMENT_TYPES = {
|
|
11
7
|
javascript: new Set(['comment']),
|
|
@@ -14,6 +10,8 @@ export const COMMENT_TYPES = {
|
|
|
14
10
|
html: new Set(['comment']),
|
|
15
11
|
css: new Set(['comment']),
|
|
16
12
|
yaml: new Set(['comment']),
|
|
13
|
+
// `.vue` `<template>` zone — tree-sitter-vue names HTML comments `comment` too.
|
|
14
|
+
vue: new Set(['comment']),
|
|
17
15
|
};
|
|
18
16
|
/**
|
|
19
17
|
* Classify every comment among its parent's named siblings onto the relevant RichNode's
|
|
@@ -37,7 +35,9 @@ function visit(node) {
|
|
|
37
35
|
visit(child);
|
|
38
36
|
classify(node);
|
|
39
37
|
}
|
|
40
|
-
|
|
38
|
+
/** Whether `node` is a comment in its grammar — the shared predicate behind keeping comments out of
|
|
39
|
+
* `children`, skipping them in container queries, and the `findComments` selector. */
|
|
40
|
+
export function isComment(node) {
|
|
41
41
|
return COMMENT_TYPES[node.language].has(node.type);
|
|
42
42
|
}
|
|
43
43
|
function classify(parent) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-attachment.js","sourceRoot":"","sources":["../src/comment-attachment.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"comment-attachment.js","sourceRoot":"","sources":["../src/comment-attachment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RichNode } from './types.js';
|
|
2
|
+
/** The opening delimiter token (`[` / `{` / `(`) of a container node. */
|
|
3
|
+
export declare function openDelimiter(node: RichNode): RichNode;
|
|
4
|
+
/** The separator token (a `,` by default, or `;` for a `;`-list) immediately after `node` among its
|
|
5
|
+
* parent's children (comments skipped), or `null` — the trailing separator `remove({ separator })`
|
|
6
|
+
* drops alongside a list element, and `append` extends to keep the container's style. */
|
|
7
|
+
export declare function trailingSeparator(node: RichNode, sep?: string): RichNode | null;
|
|
8
|
+
/** Whether `node` spans more than one line (its open and close sit on different rows) — the cue for
|
|
9
|
+
* `append`/`prepend` to lay an element on its own line rather than inline. */
|
|
10
|
+
export declare function isMultiline(node: RichNode): boolean;
|
|
11
|
+
/** Containers whose elements are separated by newlines (a block / class body), not commas. */
|
|
12
|
+
export declare const NEWLINE_CONTAINERS: Set<string>;
|
|
13
|
+
/** Containers whose members are separated/terminated by `;` (a TS interface / object type). */
|
|
14
|
+
export declare const SEMI_CONTAINERS: Set<string>;
|
|
15
|
+
//# sourceMappingURL=containers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containers.d.ts","sourceRoot":"","sources":["../src/containers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAQ1C,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAItD;AAED;;0FAE0F;AAC1F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAM,GAAG,QAAQ,GAAG,IAAI,CAW5E;AAED;+EAC+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEnD;AAED,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,aAAwD,CAAA;AAEvF,+FAA+F;AAC/F,eAAO,MAAM,eAAe,aAA6C,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isComment } from './comment-attachment.js';
|
|
2
|
+
import { assert } from './assert.js';
|
|
3
|
+
// Structural queries over container nodes (arrays / objects / blocks / interface bodies …), shared by
|
|
4
|
+
// the Collection (routing, `remove({ separator })`) and the Formatter (layout). Kept here so neither
|
|
5
|
+
// has to import the other.
|
|
6
|
+
/** The opening delimiter token (`[` / `{` / `(`) of a container node. */
|
|
7
|
+
export function openDelimiter(node) {
|
|
8
|
+
const open = node.allChildren[0];
|
|
9
|
+
assert(open, `container '${node.type}' has no opening delimiter`);
|
|
10
|
+
return open;
|
|
11
|
+
}
|
|
12
|
+
/** The separator token (a `,` by default, or `;` for a `;`-list) immediately after `node` among its
|
|
13
|
+
* parent's children (comments skipped), or `null` — the trailing separator `remove({ separator })`
|
|
14
|
+
* drops alongside a list element, and `append` extends to keep the container's style. */
|
|
15
|
+
export function trailingSeparator(node, sep = ',') {
|
|
16
|
+
const siblings = node.parent?.allChildren;
|
|
17
|
+
if (!siblings)
|
|
18
|
+
return null;
|
|
19
|
+
const i = siblings.indexOf(node);
|
|
20
|
+
if (i === -1)
|
|
21
|
+
return null;
|
|
22
|
+
for (let j = i + 1; j < siblings.length; j++) {
|
|
23
|
+
const sib = siblings[j];
|
|
24
|
+
if (isComment(sib))
|
|
25
|
+
continue;
|
|
26
|
+
return sib.type === sep ? sib : null;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/** Whether `node` spans more than one line (its open and close sit on different rows) — the cue for
|
|
31
|
+
* `append`/`prepend` to lay an element on its own line rather than inline. */
|
|
32
|
+
export function isMultiline(node) {
|
|
33
|
+
return node.startPosition.row !== node.endPosition.row;
|
|
34
|
+
}
|
|
35
|
+
/** Containers whose elements are separated by newlines (a block / class body), not commas. */
|
|
36
|
+
export const NEWLINE_CONTAINERS = new Set(['statement_block', 'class_body', 'program']);
|
|
37
|
+
/** Containers whose members are separated/terminated by `;` (a TS interface / object type). */
|
|
38
|
+
export const SEMI_CONTAINERS = new Set(['interface_body', 'object_type']);
|
|
39
|
+
//# sourceMappingURL=containers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containers.js","sourceRoot":"","sources":["../src/containers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,sGAAsG;AACtG,qGAAqG;AACrG,2BAA2B;AAE3B,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;IAChC,MAAM,CAAC,IAAI,EAAE,cAAc,IAAI,CAAC,IAAI,4BAA4B,CAAC,CAAA;IACjE,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;0FAE0F;AAC1F,MAAM,UAAU,iBAAiB,CAAC,IAAc,EAAE,GAAG,GAAG,GAAG;IACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAA;IACzC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1B,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAChC,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,SAAS,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC5B,OAAO,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;IACtC,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;AACxD,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,iBAAiB,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAA;AAEvF,+FAA+F;AAC/F,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAA"}
|
package/dist/edit-collector.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { SourceMap } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* demand, a source map) via magic-string.
|
|
3
|
+
* A pure edit buffer: collects text edits against a source string and produces the transformed code
|
|
4
|
+
* (and, on demand, a source map) via magic-string. It knows nothing about lines, indentation, or
|
|
5
|
+
* formatting — that policy lives in the {@link Formatter}, which drives this buffer.
|
|
5
6
|
*
|
|
6
|
-
* Overlap is resolved **first-wins**: an edit overlapping one already accepted is
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* position, so the generated map is precise.
|
|
7
|
+
* Overlap is resolved **first-wins**: an edit overlapping one already accepted is dropped silently —
|
|
8
|
+
* the text-layer side of outer-wins. Because edits are applied through magic-string at their original
|
|
9
|
+
* offsets, the kept text keeps its original position, so the generated map is precise.
|
|
10
10
|
*/
|
|
11
11
|
export declare class EditCollector {
|
|
12
12
|
#private;
|
|
@@ -15,19 +15,11 @@ export declare class EditCollector {
|
|
|
15
15
|
overwrite(start: number, end: number, replacement: string): void;
|
|
16
16
|
/** Delete `[start, end)`. */
|
|
17
17
|
remove(start: number, end: number): void;
|
|
18
|
-
/** Delete the whole lines `[start, end)` touches — from the start of `start`'s line (leading
|
|
19
|
-
* indentation included) through the newline after `end`'s line, so nothing blank is left behind.
|
|
20
|
-
* With `collapseBlankBefore`, also absorb whole blank lines immediately above (a separator before
|
|
21
|
-
* a dropped block). */
|
|
22
|
-
removeLines(start: number, end: number, collapseBlankBefore?: boolean): void;
|
|
23
18
|
/** Insert `text` at `index`, attached to the left chunk. A point insertion: it claims no
|
|
24
19
|
* range, so it composes with edits on either side (and survives removal of the right side). */
|
|
25
20
|
insertLeft(index: number, text: string): void;
|
|
26
21
|
/** Insert `text` at `index`, attached to the right chunk. */
|
|
27
22
|
insertRight(index: number, text: string): void;
|
|
28
|
-
/** The leading whitespace of the line containing `index` — the base indent an inserted block
|
|
29
|
-
* should match. Empty when the line starts with a non-whitespace character. */
|
|
30
|
-
indentAt(index: number): string;
|
|
31
23
|
toString(): string;
|
|
32
24
|
generateMap(source: string): SourceMap;
|
|
33
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit-collector.d.ts","sourceRoot":"","sources":["../src/edit-collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;;GAQG;AACH,qBAAa,aAAa;;
|
|
1
|
+
{"version":3,"file":"edit-collector.d.ts","sourceRoot":"","sources":["../src/edit-collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;;GAQG;AACH,qBAAa,aAAa;;gBAIZ,MAAM,EAAE,MAAM;IAI1B,yFAAyF;IACzF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAMhE,6BAA6B;IAC7B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKxC;oGACgG;IAChG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI7C,6DAA6D;IAC7D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9C,QAAQ,IAAI,MAAM;IAIlB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS;CAWvC"}
|
package/dist/edit-collector.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* demand, a source map) via magic-string.
|
|
3
|
+
* A pure edit buffer: collects text edits against a source string and produces the transformed code
|
|
4
|
+
* (and, on demand, a source map) via magic-string. It knows nothing about lines, indentation, or
|
|
5
|
+
* formatting — that policy lives in the {@link Formatter}, which drives this buffer.
|
|
5
6
|
*
|
|
6
|
-
* Overlap is resolved **first-wins**: an edit overlapping one already accepted is
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* position, so the generated map is precise.
|
|
7
|
+
* Overlap is resolved **first-wins**: an edit overlapping one already accepted is dropped silently —
|
|
8
|
+
* the text-layer side of outer-wins. Because edits are applied through magic-string at their original
|
|
9
|
+
* offsets, the kept text keeps its original position, so the generated map is precise.
|
|
10
10
|
*/
|
|
11
11
|
export class EditCollector {
|
|
12
12
|
#magic;
|
|
13
|
-
#source;
|
|
14
13
|
#claimed = [];
|
|
15
14
|
constructor(source) {
|
|
16
15
|
this.#magic = new MagicString(source);
|
|
17
|
-
this.#source = source;
|
|
18
16
|
}
|
|
19
17
|
/** Replace `[start, end)` with `replacement` (an empty range inserts before `start`). */
|
|
20
18
|
overwrite(start, end, replacement) {
|
|
@@ -31,23 +29,6 @@ export class EditCollector {
|
|
|
31
29
|
return;
|
|
32
30
|
this.#magic.remove(start, end);
|
|
33
31
|
}
|
|
34
|
-
/** Delete the whole lines `[start, end)` touches — from the start of `start`'s line (leading
|
|
35
|
-
* indentation included) through the newline after `end`'s line, so nothing blank is left behind.
|
|
36
|
-
* With `collapseBlankBefore`, also absorb whole blank lines immediately above (a separator before
|
|
37
|
-
* a dropped block). */
|
|
38
|
-
removeLines(start, end, collapseBlankBefore = false) {
|
|
39
|
-
let lineStart = this.#lineStart(start);
|
|
40
|
-
if (collapseBlankBefore) {
|
|
41
|
-
while (lineStart > 0) {
|
|
42
|
-
const prevStart = this.#lineStart(lineStart - 1);
|
|
43
|
-
if (this.#source.slice(prevStart, lineStart - 1).trim() !== '')
|
|
44
|
-
break; // a non-blank line stops it
|
|
45
|
-
lineStart = prevStart;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const newline = this.#source.indexOf('\n', end);
|
|
49
|
-
this.remove(lineStart, newline === -1 ? this.#source.length : newline + 1);
|
|
50
|
-
}
|
|
51
32
|
/** Insert `text` at `index`, attached to the left chunk. A point insertion: it claims no
|
|
52
33
|
* range, so it composes with edits on either side (and survives removal of the right side). */
|
|
53
34
|
insertLeft(index, text) {
|
|
@@ -57,21 +38,12 @@ export class EditCollector {
|
|
|
57
38
|
insertRight(index, text) {
|
|
58
39
|
this.#magic.appendRight(index, text);
|
|
59
40
|
}
|
|
60
|
-
/** The leading whitespace of the line containing `index` — the base indent an inserted block
|
|
61
|
-
* should match. Empty when the line starts with a non-whitespace character. */
|
|
62
|
-
indentAt(index) {
|
|
63
|
-
return /^[ \t]*/.exec(this.#source.slice(this.#lineStart(index), index))[0];
|
|
64
|
-
}
|
|
65
41
|
toString() {
|
|
66
42
|
return this.#magic.toString();
|
|
67
43
|
}
|
|
68
44
|
generateMap(source) {
|
|
69
45
|
return this.#magic.generateMap({ source, includeContent: true, hires: true });
|
|
70
46
|
}
|
|
71
|
-
// Offset of the first character of the line containing `index`.
|
|
72
|
-
#lineStart(index) {
|
|
73
|
-
return this.#source.lastIndexOf('\n', index - 1) + 1;
|
|
74
|
-
}
|
|
75
47
|
// Half-open intervals [start, end): reject one overlapping an accepted edit (touching
|
|
76
48
|
// boundaries don't overlap), so magic-string never sees a conflicting operation.
|
|
77
49
|
#claim(start, end) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit-collector.js","sourceRoot":"","sources":["../src/edit-collector.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,cAAc,CAAA;AAGtC;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IACf,MAAM,CAAa;IACnB,
|
|
1
|
+
{"version":3,"file":"edit-collector.js","sourceRoot":"","sources":["../src/edit-collector.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,cAAc,CAAA;AAGtC;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IACf,MAAM,CAAa;IACnB,QAAQ,GAA4B,EAAE,CAAA;IAE/C,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAA;IACvC,CAAC;IAED,yFAAyF;IACzF,SAAS,CAAC,KAAa,EAAE,GAAW,EAAE,WAAmB;QACvD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAM;QACpC,IAAI,KAAK,KAAK,GAAG;YAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;;YACxD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,WAAW,CAAC,CAAA;IAClD,CAAC;IAED,6BAA6B;IAC7B,MAAM,CAAC,KAAa,EAAE,GAAW;QAC/B,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAM;QACrD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAChC,CAAC;IAED;oGACgG;IAChG,UAAU,CAAC,KAAa,EAAE,IAAY;QACpC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACrC,CAAC;IAED,6DAA6D;IAC7D,WAAW,CAAC,KAAa,EAAE,IAAY;QACrC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACtC,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;IAC/B,CAAC;IAED,WAAW,CAAC,MAAc;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,sFAAsF;IACtF,iFAAiF;IACjF,MAAM,CAAC,KAAa,EAAE,GAAW;QAC/B,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,GAAG,CAAC,CAAC;YAAE,OAAO,KAAK,CAAA;QACtE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAA;QAChC,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|
package/dist/format.d.ts
CHANGED
|
@@ -1,13 +1,51 @@
|
|
|
1
|
-
/** A source file's
|
|
1
|
+
/** A source file's resolved formatting: the indent unit (`'\t'` or N spaces) and line ending. */
|
|
2
2
|
export interface FormatStyle {
|
|
3
3
|
indentUnit: string;
|
|
4
4
|
eol: string;
|
|
5
5
|
}
|
|
6
|
+
/** Per-apply formatting configuration (`transform(src, ctx, options)`) — each field overrides what
|
|
7
|
+
* would otherwise be detected from the source. The extension point prettier-like options (trailing
|
|
8
|
+
* comma, semicolons, quotes, print width) would grow on. */
|
|
9
|
+
export interface FormatOptions {
|
|
10
|
+
/** Force the indent unit (`'\t'` or N spaces) instead of guessing it. */
|
|
11
|
+
indentUnit?: string;
|
|
12
|
+
/** Force the line ending (`'\n'` / `'\r\n'`) instead of guessing it. */
|
|
13
|
+
eol?: string;
|
|
14
|
+
}
|
|
6
15
|
/** Guess the indent unit (most common indentation step, detect-indent style; tabs when they
|
|
7
16
|
* dominate) and EOL (first line break) of `source`, defaulting to two spaces and `'\n'`. */
|
|
8
17
|
export declare function detectStyle(source: string): FormatStyle;
|
|
18
|
+
/** The {@link FormatStyle} for an apply: detected from `source`, with any explicit `options` winning. */
|
|
19
|
+
export declare function resolveStyle(source: string, options?: FormatOptions): FormatStyle;
|
|
9
20
|
/** Re-indent a snippet for a line indented by `baseIndent`: the first line is left for the caller
|
|
10
|
-
* to position,
|
|
11
|
-
*
|
|
21
|
+
* to position, every following non-blank line is re-anchored to `baseIndent` (its indentation
|
|
22
|
+
* relative to the block's own base preserved), and line breaks become `eol`.
|
|
23
|
+
*
|
|
24
|
+
* The block's base is the least-indented continuation line — *not* the first line, which a node's
|
|
25
|
+
* `.text` leaves at column 0 even when the node sits deep in the source. Stripping that base before
|
|
26
|
+
* applying `baseIndent` re-anchors a "hanging" block (continuation lines still at their source
|
|
27
|
+
* indent) to the target, instead of stacking `baseIndent` on top of the indent already there. */
|
|
12
28
|
export declare function reindent(text: string, baseIndent: string, eol: string): string;
|
|
29
|
+
/** Offset of the first character of the line containing `index`. */
|
|
30
|
+
export declare function lineStartOf(source: string, index: number): number;
|
|
31
|
+
/** The leading whitespace of the line containing `index` — the base indent an inserted block should
|
|
32
|
+
* match. Empty when the line starts with a non-whitespace character. */
|
|
33
|
+
export declare function indentOf(source: string, index: number): string;
|
|
34
|
+
/** A space or tab — the horizontal whitespace separating inline list elements (`undefined` past
|
|
35
|
+
* either end of the source is not whitespace). */
|
|
36
|
+
export declare function isHSpace(char: string | undefined): boolean;
|
|
37
|
+
/** The start of the run of whole blank lines immediately above the line beginning at `lineStart`
|
|
38
|
+
* — `lineStart` itself when the preceding line is non-blank. The "absorb blank lines above" step
|
|
39
|
+
* shared by whole-line removal and the line-collapse of a removed last element. */
|
|
40
|
+
export declare function blankRunStart(source: string, lineStart: number): number;
|
|
41
|
+
/** The end of the run of whole blank lines beginning at line start `from` — the start of the first
|
|
42
|
+
* non-blank line at or after it (`from` itself when that line is non-blank, the source length when
|
|
43
|
+
* only blank lines remain). The forward mirror of {@link blankRunStart}: collapsing the blank lines
|
|
44
|
+
* that followed a removed first element, which would dangle after the container's opening delimiter. */
|
|
45
|
+
export declare function blankRunEnd(source: string, from: number): number;
|
|
46
|
+
/** The whole-line span `[from, to)` covering the lines `[start, end)` touches — from the start of
|
|
47
|
+
* `start`'s line (leading indentation included) through the newline after `end`'s line, so nothing
|
|
48
|
+
* blank is left behind. With `collapseBlankBefore`, also absorb whole blank lines immediately above
|
|
49
|
+
* (a separator before a dropped block). */
|
|
50
|
+
export declare function wholeLineRange(source: string, start: number, end: number, collapseBlankBefore?: boolean): [number, number];
|
|
13
51
|
//# sourceMappingURL=format.d.ts.map
|
package/dist/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAIA,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;6DAE6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;6FAC6F;AAC7F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAEvD;AAED,yGAAyG;AACzG,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,WAAW,CAMjF;AA0CD;;;;;;;kGAOkG;AAClG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAU9E;AAKD,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;yEACyE;AACzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;mDACmD;AACnD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE1D;AAED;;oFAEoF;AACpF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQvE;AAED;;;yGAGyG;AACzG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAShE;AAED;;;4CAG4C;AAC5C,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,mBAAmB,UAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxH"}
|
package/dist/format.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
// Guessing a file's formatting and re-indenting inserted code. Codegraft edits byte ranges, so
|
|
2
2
|
// code it doesn't touch keeps its formatting for free; these let an *inserted* snippet adopt the
|
|
3
|
-
// file's indent unit and line ending instead of landing at column 0.
|
|
3
|
+
// file's indent unit and line ending instead of landing at column 0.
|
|
4
4
|
/** Guess the indent unit (most common indentation step, detect-indent style; tabs when they
|
|
5
5
|
* dominate) and EOL (first line break) of `source`, defaulting to two spaces and `'\n'`. */
|
|
6
6
|
export function detectStyle(source) {
|
|
7
7
|
return { indentUnit: detectIndentUnit(source), eol: detectEol(source) };
|
|
8
8
|
}
|
|
9
|
+
/** The {@link FormatStyle} for an apply: detected from `source`, with any explicit `options` winning. */
|
|
10
|
+
export function resolveStyle(source, options) {
|
|
11
|
+
const detected = detectStyle(source);
|
|
12
|
+
return {
|
|
13
|
+
indentUnit: options?.indentUnit ?? detected.indentUnit,
|
|
14
|
+
eol: options?.eol ?? detected.eol,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
9
17
|
function detectEol(source) {
|
|
10
18
|
return /\r\n|\n/.exec(source)?.[0] ?? '\n';
|
|
11
19
|
}
|
|
@@ -47,14 +55,79 @@ function detectIndentUnit(source) {
|
|
|
47
55
|
return ' '.repeat(unit || 2);
|
|
48
56
|
}
|
|
49
57
|
/** Re-indent a snippet for a line indented by `baseIndent`: the first line is left for the caller
|
|
50
|
-
* to position,
|
|
51
|
-
*
|
|
58
|
+
* to position, every following non-blank line is re-anchored to `baseIndent` (its indentation
|
|
59
|
+
* relative to the block's own base preserved), and line breaks become `eol`.
|
|
60
|
+
*
|
|
61
|
+
* The block's base is the least-indented continuation line — *not* the first line, which a node's
|
|
62
|
+
* `.text` leaves at column 0 even when the node sits deep in the source. Stripping that base before
|
|
63
|
+
* applying `baseIndent` re-anchors a "hanging" block (continuation lines still at their source
|
|
64
|
+
* indent) to the target, instead of stacking `baseIndent` on top of the indent already there. */
|
|
52
65
|
export function reindent(text, baseIndent, eol) {
|
|
53
66
|
if (!text.includes('\n'))
|
|
54
67
|
return text;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
68
|
+
const lines = text.split(/\r\n|\n/);
|
|
69
|
+
let base = Infinity;
|
|
70
|
+
for (let i = 1; i < lines.length; i++) {
|
|
71
|
+
if (lines[i].trim() === '')
|
|
72
|
+
continue;
|
|
73
|
+
base = Math.min(base, /^[ \t]*/.exec(lines[i])[0].length);
|
|
74
|
+
}
|
|
75
|
+
if (base === Infinity)
|
|
76
|
+
base = 0; // a single-or-blank-continuation snippet: nothing to strip
|
|
77
|
+
return lines.map((line, i) => (i === 0 || line.trim() === '' ? line : baseIndent + line.slice(base))).join(eol);
|
|
78
|
+
}
|
|
79
|
+
// —— Pure line/whitespace queries over a source string (shared by the formatter and whole-line
|
|
80
|
+
// removal). They take the source explicitly so they stay independent of the edit buffer. ——
|
|
81
|
+
/** Offset of the first character of the line containing `index`. */
|
|
82
|
+
export function lineStartOf(source, index) {
|
|
83
|
+
return source.lastIndexOf('\n', index - 1) + 1;
|
|
84
|
+
}
|
|
85
|
+
/** The leading whitespace of the line containing `index` — the base indent an inserted block should
|
|
86
|
+
* match. Empty when the line starts with a non-whitespace character. */
|
|
87
|
+
export function indentOf(source, index) {
|
|
88
|
+
return /^[ \t]*/.exec(source.slice(lineStartOf(source, index), index))[0];
|
|
89
|
+
}
|
|
90
|
+
/** A space or tab — the horizontal whitespace separating inline list elements (`undefined` past
|
|
91
|
+
* either end of the source is not whitespace). */
|
|
92
|
+
export function isHSpace(char) {
|
|
93
|
+
return char === ' ' || char === '\t';
|
|
94
|
+
}
|
|
95
|
+
/** The start of the run of whole blank lines immediately above the line beginning at `lineStart`
|
|
96
|
+
* — `lineStart` itself when the preceding line is non-blank. The "absorb blank lines above" step
|
|
97
|
+
* shared by whole-line removal and the line-collapse of a removed last element. */
|
|
98
|
+
export function blankRunStart(source, lineStart) {
|
|
99
|
+
let from = lineStart;
|
|
100
|
+
while (from > 0) {
|
|
101
|
+
const prevStart = lineStartOf(source, from - 1);
|
|
102
|
+
if (source.slice(prevStart, from - 1).trim() !== '')
|
|
103
|
+
break; // a non-blank line stops it
|
|
104
|
+
from = prevStart;
|
|
105
|
+
}
|
|
106
|
+
return from;
|
|
107
|
+
}
|
|
108
|
+
/** The end of the run of whole blank lines beginning at line start `from` — the start of the first
|
|
109
|
+
* non-blank line at or after it (`from` itself when that line is non-blank, the source length when
|
|
110
|
+
* only blank lines remain). The forward mirror of {@link blankRunStart}: collapsing the blank lines
|
|
111
|
+
* that followed a removed first element, which would dangle after the container's opening delimiter. */
|
|
112
|
+
export function blankRunEnd(source, from) {
|
|
113
|
+
let to = from;
|
|
114
|
+
while (to < source.length) {
|
|
115
|
+
const newline = source.indexOf('\n', to);
|
|
116
|
+
const lineEnd = newline === -1 ? source.length : newline;
|
|
117
|
+
if (source.slice(to, lineEnd).trim() !== '')
|
|
118
|
+
break; // a non-blank line stops it
|
|
119
|
+
to = newline === -1 ? source.length : newline + 1;
|
|
120
|
+
}
|
|
121
|
+
return to;
|
|
122
|
+
}
|
|
123
|
+
/** The whole-line span `[from, to)` covering the lines `[start, end)` touches — from the start of
|
|
124
|
+
* `start`'s line (leading indentation included) through the newline after `end`'s line, so nothing
|
|
125
|
+
* blank is left behind. With `collapseBlankBefore`, also absorb whole blank lines immediately above
|
|
126
|
+
* (a separator before a dropped block). */
|
|
127
|
+
export function wholeLineRange(source, start, end, collapseBlankBefore = false) {
|
|
128
|
+
const lineStart = lineStartOf(source, start);
|
|
129
|
+
const from = collapseBlankBefore ? blankRunStart(source, lineStart) : lineStart;
|
|
130
|
+
const newline = source.indexOf('\n', end);
|
|
131
|
+
return [from, newline === -1 ? source.length : newline + 1];
|
|
59
132
|
}
|
|
60
133
|
//# sourceMappingURL=format.js.map
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;AACjG,
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { RichNode } from './types.js';
|
|
2
|
+
import type { EditCollector } from './edit-collector.js';
|
|
3
|
+
import { type FormatStyle } from './format.js';
|
|
4
|
+
/**
|
|
5
|
+
* The layout-formatting policy: how an edit is *rendered* once the codemod has decided *what* to
|
|
6
|
+
* edit. Built per-apply from the source plus a resolved {@link FormatStyle}. The {@link Collection}
|
|
7
|
+
* delegates the rendering of each edit to it; the {@link EditCollector} it drives stays a pure edit
|
|
8
|
+
* buffer.
|
|
9
|
+
*
|
|
10
|
+
* This is the single home for indentation/EOL re-rendering, container layout (separators, trailing
|
|
11
|
+
* commas, brace padding), and line-collapse on removal — and the place prettier-like options
|
|
12
|
+
* (trailing-comma/semicolon/quote/print-width) would grow.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Formatter {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(collector: EditCollector, source: string, style: FormatStyle);
|
|
17
|
+
/** The line ending — for inserting whole statements (`ensureImport`). */
|
|
18
|
+
get eol(): string;
|
|
19
|
+
/** The leading whitespace of the line containing `index` — for restoring a displaced node's indent. */
|
|
20
|
+
indentAt(index: number): string;
|
|
21
|
+
/** `text` re-indented to the line at `index` (single-line text is unchanged) — for a replaced or
|
|
22
|
+
* inserted snippet. */
|
|
23
|
+
reindent(text: string, index: number): string;
|
|
24
|
+
/** Append `text` as the last element of `node`: a fresh indented line in a block/class body, after
|
|
25
|
+
* the last element of a comma- or `;`-separated list, or as the sole element of an empty one. */
|
|
26
|
+
append(node: RichNode, text: string): void;
|
|
27
|
+
/** Prepend `text` as the first element of `node` — the mirror of {@link append}. */
|
|
28
|
+
prepend(node: RichNode, text: string): void;
|
|
29
|
+
/** Delete `[start, end)`, collapsing whitespace the way Prettier would: when the span owns its
|
|
30
|
+
* line(s) — only indentation before `start`, only whitespace after `end` up to the line break —
|
|
31
|
+
* drop the whole lines so no blank line is left; otherwise an in-line delete that also clears one
|
|
32
|
+
* residual separating space, so a removed mid-list element (`[1, two(), 3]`) leaves `[1, 3]`, not
|
|
33
|
+
* `[1, 3]`.
|
|
34
|
+
*
|
|
35
|
+
* In the own-line case the content, the leading indent, and the trailing line break are removed
|
|
36
|
+
* separately so the deletion composes after an abutting edit: a prior `unwrap`/`dropDirective` that
|
|
37
|
+
* already consumed the run up to `start` leaves the content abutting it (no overlap, so it lands),
|
|
38
|
+
* while the leading-indent removal it overlaps is dropped on its own (first-wins) instead of taking
|
|
39
|
+
* the whole line removal down with it.
|
|
40
|
+
*
|
|
41
|
+
* `collapse.before`/`collapse.after` (the node was the last / first surviving element of its
|
|
42
|
+
* container) additionally drop the run of blank lines directly above / below: a blank separator
|
|
43
|
+
* left against the container's closing / opening delimiter, which Prettier strips. Each is its own
|
|
44
|
+
* edit, abutting (not overlapping) the line removal, so a prior edit on the line is unaffected. */
|
|
45
|
+
removeNode(start: number, end: number, collapse?: {
|
|
46
|
+
before?: boolean;
|
|
47
|
+
after?: boolean;
|
|
48
|
+
}): void;
|
|
49
|
+
/** Delete `[start, end)` where `end` begins a following node, collapsing the lines before it: when
|
|
50
|
+
* `start` opens its own line and `end` sits on a later line, drop `[start's line, end's line)` so
|
|
51
|
+
* the leading run (a directive comment and any comments stacked under it) vanishes whole-line while
|
|
52
|
+
* `end`'s own line — its indentation included — is left for a later edit to collapse independently.
|
|
53
|
+
* Otherwise (an inline `start`, or `end` on the same line) a plain `[start, end)` delete. Used by
|
|
54
|
+
* `dropDirective` so it composes with a following `remove`. */
|
|
55
|
+
removeLeadingTo(start: number, end: number): void;
|
|
56
|
+
/** Delete the whole lines `[start, end)` touches — leading indentation through the trailing newline,
|
|
57
|
+
* so nothing blank is left. With `collapseBlankBefore`, also absorb whole blank lines immediately
|
|
58
|
+
* above (a separator before a dropped block). The explicit whole-line removal `remove({ wholeLines })`
|
|
59
|
+
* asks for, where collapsing per node isn't enough. */
|
|
60
|
+
removeWholeLines(start: number, end: number, collapseBlankBefore?: boolean): void;
|
|
61
|
+
/** Register a dedent of the lifted range `[from, to)` by `dedent` columns: every continuation line
|
|
62
|
+
* (the lines after the first) loses up to `dedent` leading-whitespace columns. Used by `unwrap`,
|
|
63
|
+
* whose kept statements drop a level — their first line inherits the wrapper's indent for free,
|
|
64
|
+
* but the rest keep their deeper source indent.
|
|
65
|
+
*
|
|
66
|
+
* Deferred to {@link flush} rather than applied now so it yields (first-wins) to the codemod's own
|
|
67
|
+
* edits on the kept nodes: a kept statement removed or replaced in the same pass wins the overlap,
|
|
68
|
+
* so only lines that actually survive get dedented. A no-op unless `dedent` is positive. */
|
|
69
|
+
deferReindent(from: number, to: number, dedent: number): void;
|
|
70
|
+
/** Apply every {@link deferReindent}, once, after the codemod has recorded its explicit edits. */
|
|
71
|
+
flush(): void;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,KAAK,WAAW,EAAyF,MAAM,aAAa,CAAA;AAGrI;;;;;;;;;GASG;AACH,qBAAa,SAAS;;gBAQR,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW;IAMxE,yEAAyE;IACzE,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,uGAAuG;IACvG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI/B;4BACwB;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAM7C;sGACkG;IAClG,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAY1C,oFAAoF;IACpF,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAW3C;;;;;;;;;;;;;;;wGAeoG;IACpG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI;IAuBlG;;;;;oEAKgE;IAChE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAOjD;;;4DAGwD;IACxD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,mBAAmB,UAAQ,GAAG,IAAI;IAO/E;;;;;;;iGAO6F;IAC7F,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAI7D,kGAAkG;IAClG,KAAK,IAAI,IAAI;CA+Ed"}
|