@malloydata/malloy 0.0.384 → 0.0.386

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.
Files changed (36) hide show
  1. package/dist/api/foundation/config_resolve.js +12 -20
  2. package/dist/internal.d.ts +2 -0
  3. package/dist/internal.js +3 -1
  4. package/dist/lang/prettify/binary-chain.d.ts +3 -0
  5. package/dist/lang/prettify/binary-chain.js +65 -0
  6. package/dist/lang/prettify/block-body.d.ts +4 -0
  7. package/dist/lang/prettify/block-body.js +74 -0
  8. package/dist/lang/prettify/error-listener.d.ts +6 -0
  9. package/dist/lang/prettify/error-listener.js +19 -0
  10. package/dist/lang/prettify/field-properties.d.ts +3 -0
  11. package/dist/lang/prettify/field-properties.js +57 -0
  12. package/dist/lang/prettify/formatter.d.ts +17 -0
  13. package/dist/lang/prettify/formatter.js +145 -0
  14. package/dist/lang/prettify/index.d.ts +19 -0
  15. package/dist/lang/prettify/index.js +139 -0
  16. package/dist/lang/prettify/inline-renderer.d.ts +3 -0
  17. package/dist/lang/prettify/inline-renderer.js +101 -0
  18. package/dist/lang/prettify/leaf.d.ts +9 -0
  19. package/dist/lang/prettify/leaf.js +313 -0
  20. package/dist/lang/prettify/out.d.ts +12 -0
  21. package/dist/lang/prettify/out.js +74 -0
  22. package/dist/lang/prettify/pick-case.d.ts +5 -0
  23. package/dist/lang/prettify/pick-case.js +222 -0
  24. package/dist/lang/prettify/rules.d.ts +13 -0
  25. package/dist/lang/prettify/rules.js +103 -0
  26. package/dist/lang/prettify/sections.d.ts +4 -0
  27. package/dist/lang/prettify/sections.js +261 -0
  28. package/dist/lang/prettify/tokens.d.ts +13 -0
  29. package/dist/lang/prettify/tokens.js +160 -0
  30. package/dist/lang/prettify/types.d.ts +9 -0
  31. package/dist/lang/prettify/types.js +7 -0
  32. package/dist/lang/run-malloy-parser.d.ts +23 -0
  33. package/dist/lang/run-malloy-parser.js +32 -8
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +4 -4
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * RULE: PICK — formatPickStatement / formatPickAligned / formatPick
7
+ *
8
+ * pickStatement: pick+ (ELSE pickElse=fieldExpr)?
9
+ *
10
+ * Inline if the whole thing fits. Otherwise:
11
+ * - each pick on its own line at +1 indent
12
+ * - column-align WHEN across picks (pad shorter values)
13
+ * - else aligns with `pick` (no value padding for else)
14
+ * - if a single pick still doesn't fit when aligned, break that pick at WHEN
15
+ * onto two lines.
16
+ *
17
+ * RULE: CASE — formatCaseStatement / formatCaseWhen
18
+ *
19
+ * caseStatement: CASE valueExpr? (caseWhen)+ (ELSE caseElse)? END
20
+ *
21
+ * Inline if it fits. Otherwise:
22
+ * case [valueExpr]
23
+ * when COND_A then RESULT_A
24
+ * when LONGCOND then RESULT_B
25
+ * else FALLBACK
26
+ * end
27
+ * THEN keyword column-aligns across whens (pad shorter conditions). ELSE/END
28
+ * live at the case's own indent. If an aligned when still overflows, break it
29
+ * at THEN onto two lines.
30
+ */
31
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
32
+ if (k2 === undefined) k2 = k;
33
+ var desc = Object.getOwnPropertyDescriptor(m, k);
34
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
35
+ desc = { enumerable: true, get: function() { return m[k]; } };
36
+ }
37
+ Object.defineProperty(o, k2, desc);
38
+ }) : (function(o, m, k, k2) {
39
+ if (k2 === undefined) k2 = k;
40
+ o[k2] = m[k];
41
+ }));
42
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
43
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
44
+ }) : function(o, v) {
45
+ o["default"] = v;
46
+ });
47
+ var __importStar = (this && this.__importStar) || (function () {
48
+ var ownKeys = function(o) {
49
+ ownKeys = Object.getOwnPropertyNames || function (o) {
50
+ var ar = [];
51
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
52
+ return ar;
53
+ };
54
+ return ownKeys(o);
55
+ };
56
+ return function (mod) {
57
+ if (mod && mod.__esModule) return mod;
58
+ var result = {};
59
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
60
+ __setModuleDefault(result, mod);
61
+ return result;
62
+ };
63
+ })();
64
+ Object.defineProperty(exports, "__esModule", { value: true });
65
+ exports.formatPickStatement = formatPickStatement;
66
+ exports.formatPick = formatPick;
67
+ exports.formatCaseStatement = formatCaseStatement;
68
+ const tree_1 = require("antlr4ts/tree");
69
+ const parser = __importStar(require("../lib/Malloy/MalloyParser"));
70
+ const tokens_1 = require("./tokens");
71
+ const leaf_1 = require("./leaf");
72
+ const inline_renderer_1 = require("./inline-renderer");
73
+ function formatPickStatement(f, ctx) {
74
+ const startIdx = ctx._start.tokenIndex;
75
+ const stopIdx = ctx._stop.tokenIndex;
76
+ const inlineLen = (0, leaf_1.approxInlineSpan)(f, startIdx, stopIdx);
77
+ const hasComments = (0, leaf_1.hasCommentsInRange)(f, startIdx, stopIdx);
78
+ if (!hasComments && f.o.lineLengthSoFar() + 1 + inlineLen <= tokens_1.LINE_BUDGET) {
79
+ for (let i = 0; i < ctx.childCount; i++)
80
+ f.format(ctx.getChild(i));
81
+ return;
82
+ }
83
+ const picks = ctx.pick();
84
+ const elseTok = ctx.ELSE();
85
+ const elseExpr = ctx.fieldExpr();
86
+ const valueStrs = picks.map(p => p._pickValue ? (0, inline_renderer_1.renderItemInline)(f, p._pickValue) : '');
87
+ const maxValueLen = valueStrs.reduce((m, s) => Math.max(m, s.length), 0);
88
+ // Use flushHiddenBefore (the leaf walker's emitter) to flush gap comments
89
+ // between picks. Critical: it advances f.lastEmittedIdx, so when the next
90
+ // pick takes the broken-at-WHEN form (which goes through f.format → emit-
91
+ // VisibleToken → flushHiddenBefore), the gap won't be re-emitted.
92
+ f.o.indent++;
93
+ for (let i = 0; i < picks.length; i++) {
94
+ (0, leaf_1.flushHiddenBefore)(f, picks[i]._start.tokenIndex);
95
+ f.o.nl();
96
+ formatPickAligned(f, picks[i], maxValueLen);
97
+ }
98
+ if (elseTok && elseExpr) {
99
+ (0, leaf_1.flushHiddenBefore)(f, elseTok.symbol.tokenIndex);
100
+ f.o.nl();
101
+ f.format(elseTok);
102
+ f.format(elseExpr);
103
+ }
104
+ f.o.indent--;
105
+ }
106
+ function formatPickAligned(f, pick, maxValueLen) {
107
+ const valueStr = pick._pickValue ? (0, inline_renderer_1.renderItemInline)(f, pick._pickValue) : '';
108
+ const condStr = (0, inline_renderer_1.renderItemInline)(f, pick._pickWhen);
109
+ const padding = ' '.repeat(maxValueLen - valueStr.length);
110
+ const aligned = valueStr
111
+ ? `pick ${valueStr}${padding} when ${condStr}`
112
+ : `pick${padding ? ' ' + padding : ''} when ${condStr}`;
113
+ // Aligned form drops comments (renderItemInline skips them). If this pick
114
+ // has internal comments, fall through to the broken-at-WHEN form which uses
115
+ // f.format() and preserves them.
116
+ const hasComments = (0, leaf_1.hasCommentsInRange)(f, pick._start.tokenIndex, pick._stop.tokenIndex);
117
+ if (!hasComments && f.o.lineLengthSoFar() + aligned.length <= tokens_1.LINE_BUDGET) {
118
+ // Aligned form: emit as a single pre-rendered string so the spacing
119
+ // between value and `when` is exact regardless of padding width.
120
+ f.o.text(aligned);
121
+ (0, leaf_1.note)(f, pick._stop.type, pick._stop.tokenIndex, pick._stop);
122
+ return;
123
+ }
124
+ // Doesn't fit even aligned — break at WHEN, no padding.
125
+ f.format(pick.PICK());
126
+ if (pick._pickValue)
127
+ f.format(pick._pickValue);
128
+ // Flush hidden tokens between value and WHEN BEFORE the line break, so
129
+ // same-line comments stay attached to the value's line and the formatted
130
+ // output is idempotent on a re-parse.
131
+ (0, leaf_1.flushHiddenBefore)(f, pick.WHEN().symbol.tokenIndex);
132
+ f.o.nl();
133
+ f.format(pick.WHEN());
134
+ f.format(pick._pickWhen);
135
+ }
136
+ // pick: PICK pickValue? WHEN pickWhen
137
+ // Inline if fits; otherwise break at WHEN. (Used when this pick is dispatched
138
+ // outside of a pickStatement context, which is rare but possible.)
139
+ function formatPick(f, ctx) {
140
+ const inlineLen = (0, leaf_1.approxInlineSpan)(f, ctx._start.tokenIndex, ctx._stop.tokenIndex);
141
+ if (f.o.lineLengthSoFar() + 1 + inlineLen <= tokens_1.LINE_BUDGET) {
142
+ for (let i = 0; i < ctx.childCount; i++)
143
+ f.format(ctx.getChild(i));
144
+ return;
145
+ }
146
+ for (let i = 0; i < ctx.childCount; i++) {
147
+ const c = ctx.getChild(i);
148
+ if (c instanceof tree_1.TerminalNode && c.symbol.type === tokens_1.L.WHEN) {
149
+ // Flush any hidden tokens between the previous child and WHEN before
150
+ // the line break, so same-line comments stay attached.
151
+ (0, leaf_1.flushHiddenBefore)(f, c.symbol.tokenIndex);
152
+ f.o.nl();
153
+ }
154
+ f.format(c);
155
+ }
156
+ }
157
+ function formatCaseStatement(f, ctx) {
158
+ const startIdx = ctx._start.tokenIndex;
159
+ const stopIdx = ctx._stop.tokenIndex;
160
+ const inlineLen = (0, leaf_1.approxInlineSpan)(f, startIdx, stopIdx);
161
+ const hasComments = (0, leaf_1.hasCommentsInRange)(f, startIdx, stopIdx);
162
+ if (!hasComments && f.o.lineLengthSoFar() + 1 + inlineLen <= tokens_1.LINE_BUDGET) {
163
+ for (let i = 0; i < ctx.childCount; i++)
164
+ f.format(ctx.getChild(i));
165
+ return;
166
+ }
167
+ const whens = ctx.caseWhen();
168
+ const condStrs = whens.map(w => (0, inline_renderer_1.renderItemInline)(f, w._condition));
169
+ const maxCondLen = condStrs.reduce((m, s) => Math.max(m, s.length), 0);
170
+ f.o.indent++;
171
+ for (let i = 0; i < ctx.childCount; i++) {
172
+ const c = ctx.getChild(i);
173
+ if (c instanceof parser.CaseWhenContext) {
174
+ (0, leaf_1.flushHiddenBefore)(f, c._start.tokenIndex);
175
+ f.o.nl();
176
+ formatCaseWhen(f, c, maxCondLen);
177
+ continue;
178
+ }
179
+ if (c instanceof tree_1.TerminalNode && c.symbol.type === tokens_1.L.ELSE) {
180
+ (0, leaf_1.flushHiddenBefore)(f, c.symbol.tokenIndex);
181
+ f.o.nl();
182
+ f.format(c);
183
+ // emit the else expression on the same line
184
+ i++;
185
+ if (i < ctx.childCount)
186
+ f.format(ctx.getChild(i));
187
+ continue;
188
+ }
189
+ if (c instanceof tree_1.TerminalNode && c.symbol.type === tokens_1.L.END) {
190
+ (0, leaf_1.flushHiddenBefore)(f, c.symbol.tokenIndex);
191
+ f.o.indent--;
192
+ f.o.nl();
193
+ f.format(c);
194
+ f.o.indent++;
195
+ continue;
196
+ }
197
+ // CASE keyword and optional valueExpr stay on the head line.
198
+ f.format(c);
199
+ }
200
+ f.o.indent--;
201
+ }
202
+ function formatCaseWhen(f, ctx, maxCondLen) {
203
+ const condStr = (0, inline_renderer_1.renderItemInline)(f, ctx._condition);
204
+ const resultStr = (0, inline_renderer_1.renderItemInline)(f, ctx._result);
205
+ const padding = ' '.repeat(maxCondLen - condStr.length);
206
+ const aligned = `when ${condStr}${padding} then ${resultStr}`;
207
+ const hasComments = (0, leaf_1.hasCommentsInRange)(f, ctx._start.tokenIndex, ctx._stop.tokenIndex);
208
+ if (!hasComments && f.o.lineLengthSoFar() + aligned.length <= tokens_1.LINE_BUDGET) {
209
+ f.o.text(aligned);
210
+ (0, leaf_1.note)(f, ctx._stop.type, ctx._stop.tokenIndex, ctx._stop);
211
+ return;
212
+ }
213
+ // Doesn't fit aligned — break at THEN. Flush comments between condition
214
+ // and THEN before the break for same-line attachment.
215
+ f.format(ctx.WHEN());
216
+ f.format(ctx._condition);
217
+ (0, leaf_1.flushHiddenBefore)(f, ctx.THEN().symbol.tokenIndex);
218
+ f.o.nl();
219
+ f.format(ctx.THEN());
220
+ f.format(ctx._result);
221
+ }
222
+ //# sourceMappingURL=pick-case.js.map
@@ -0,0 +1,13 @@
1
+ import type { ParserRuleContext } from 'antlr4ts';
2
+ export type ItemKind = 'fieldEntry' | 'nestEntry' | 'fieldDef' | 'fieldName' | 'collectionMember' | 'orderBySpec' | 'fieldExpr';
3
+ export interface SectionRule {
4
+ ctxClass: new (...args: never[]) => ParserRuleContext;
5
+ keywordTypes: number[];
6
+ list: (ctx: ParserRuleContext) => ParserRuleContext | undefined;
7
+ itemKind: ItemKind;
8
+ }
9
+ export declare const SECTION_STATEMENT_RULES: SectionRule[];
10
+ export declare const STATEMENT_KIND_BY_CTX: Array<{
11
+ ctxClass: new (...args: never[]) => ParserRuleContext;
12
+ kind: string;
13
+ }>;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Section-statement dispatch table and statement-kind labels.
7
+ *
8
+ * SECTION_STATEMENT_RULES is the table of `keyword: items` rules that need
9
+ * flow-fill, column alignment, or annotation-aware item handling. Section
10
+ * keywords NOT in this table fall through to the leaf walker, which produces
11
+ * correct-but-plain output. Add a row here only when the default isn't good
12
+ * enough.
13
+ *
14
+ * STATEMENT_KIND_BY_CTX is the coarse grouping used by the block-body rule
15
+ * to decide whether two adjacent statements should preserve a user-supplied
16
+ * blank line (different kinds: yes; same kind: no).
17
+ *
18
+ * A maintainer adding a new section-statement lands here first.
19
+ */
20
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ var desc = Object.getOwnPropertyDescriptor(m, k);
23
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
24
+ desc = { enumerable: true, get: function() { return m[k]; } };
25
+ }
26
+ Object.defineProperty(o, k2, desc);
27
+ }) : (function(o, m, k, k2) {
28
+ if (k2 === undefined) k2 = k;
29
+ o[k2] = m[k];
30
+ }));
31
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
32
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
33
+ }) : function(o, v) {
34
+ o["default"] = v;
35
+ });
36
+ var __importStar = (this && this.__importStar) || (function () {
37
+ var ownKeys = function(o) {
38
+ ownKeys = Object.getOwnPropertyNames || function (o) {
39
+ var ar = [];
40
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
41
+ return ar;
42
+ };
43
+ return ownKeys(o);
44
+ };
45
+ return function (mod) {
46
+ if (mod && mod.__esModule) return mod;
47
+ var result = {};
48
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
49
+ __setModuleDefault(result, mod);
50
+ return result;
51
+ };
52
+ })();
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.STATEMENT_KIND_BY_CTX = exports.SECTION_STATEMENT_RULES = void 0;
55
+ const parser = __importStar(require("../lib/Malloy/MalloyParser"));
56
+ const tokens_1 = require("./tokens");
57
+ // Constructs a SectionRule with the list-accessor closure typed against the
58
+ // concrete context class. The single internal narrowing is justified: the
59
+ // dispatcher only invokes `rule.list(node)` after `node instanceof
60
+ // rule.ctxClass`, so at runtime the parameter really is of type `C`.
61
+ function rule(ctxClass, keywordTypes, list, itemKind) {
62
+ return {
63
+ ctxClass,
64
+ keywordTypes,
65
+ list: ctx => list(ctx),
66
+ itemKind,
67
+ };
68
+ }
69
+ exports.SECTION_STATEMENT_RULES = [
70
+ rule(parser.AggregateStatementContext, [tokens_1.L.AGGREGATE], c => c.queryFieldList(), 'fieldEntry'),
71
+ rule(parser.GroupByStatementContext, [tokens_1.L.GROUP_BY], c => c.queryFieldList(), 'fieldEntry'),
72
+ rule(parser.CalculateStatementContext, [tokens_1.L.CALCULATE], c => c.queryFieldList(), 'fieldEntry'),
73
+ rule(parser.NestStatementContext, [tokens_1.L.NEST], c => c.nestedQueryList(), 'nestEntry'),
74
+ rule(parser.DeclareStatementContext, [tokens_1.L.DECLARE], c => c.defList(), 'fieldDef'),
75
+ rule(parser.DefMeasuresContext, [tokens_1.L.MEASURE], c => c.defList(), 'fieldDef'),
76
+ rule(parser.DefDimensionsContext, [tokens_1.L.DIMENSION], c => c.defList(), 'fieldDef'),
77
+ rule(parser.DefExploreEditFieldContext, [tokens_1.L.ACCEPT, tokens_1.L.EXCEPT], c => c.fieldNameList(), 'fieldName'),
78
+ rule(parser.ProjectStatementContext, [tokens_1.L.SELECT], c => c.fieldCollection(), 'collectionMember'),
79
+ rule(parser.OrderByStatementContext, [tokens_1.L.ORDER_BY], c => c.ordering(), 'orderBySpec'),
80
+ rule(parser.WhereStatementContext, [tokens_1.L.WHERE], c => c.filterClauseList(), 'fieldExpr'),
81
+ rule(parser.HavingStatementContext, [tokens_1.L.HAVING], c => c.filterClauseList(), 'fieldExpr'),
82
+ ];
83
+ // Coarse statement-kind labels for the same-kind-no-blank rule in block
84
+ // bodies. Different kinds preserve a single user-supplied blank line.
85
+ exports.STATEMENT_KIND_BY_CTX = [
86
+ { ctxClass: parser.JoinStatementContext, kind: 'join' },
87
+ { ctxClass: parser.QueryJoinStatementContext, kind: 'join' },
88
+ { ctxClass: parser.DefMeasuresContext, kind: 'measure' },
89
+ { ctxClass: parser.DefDimensionsContext, kind: 'dimension' },
90
+ { ctxClass: parser.DeclareStatementContext, kind: 'declare' },
91
+ { ctxClass: parser.AggregateStatementContext, kind: 'aggregate' },
92
+ { ctxClass: parser.GroupByStatementContext, kind: 'group_by' },
93
+ { ctxClass: parser.CalculateStatementContext, kind: 'calculate' },
94
+ { ctxClass: parser.NestStatementContext, kind: 'nest' },
95
+ { ctxClass: parser.WhereStatementContext, kind: 'where' },
96
+ { ctxClass: parser.HavingStatementContext, kind: 'having' },
97
+ { ctxClass: parser.OrderByStatementContext, kind: 'order_by' },
98
+ { ctxClass: parser.ProjectStatementContext, kind: 'select' },
99
+ { ctxClass: parser.DefExploreEditFieldContext, kind: 'edit_field' },
100
+ { ctxClass: parser.DefExploreRenameContext, kind: 'rename' },
101
+ { ctxClass: parser.DefExploreQueryContext, kind: 'view' },
102
+ ];
103
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1,4 @@
1
+ import { ParserRuleContext } from 'antlr4ts';
2
+ import type { Formatter } from './formatter';
3
+ import type { SectionRule } from './rules';
4
+ export declare function formatSectionStatement(f: Formatter, stmt: ParserRuleContext, rule: SectionRule): void;
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * RULE: SECTION-STATEMENT — formatSectionStatement / formatSectionList
7
+ *
8
+ * A `keyword: items` block. The statement context wraps:
9
+ * <tags?> <ACCESS_LABEL?> <KEYWORD> <listCtx>
10
+ * We walk children: tags + access-label go through the normal dispatcher
11
+ * (so annotations are preserved); the KEYWORD is emitted manually; the
12
+ * list context dispatches to formatSectionList.
13
+ *
14
+ * formatSectionList rule (locked in with the user):
15
+ * - All bare items + total fits ≤ LINE_BUDGET → inline `kw: a, b, c`.
16
+ * - Single item that fits (even if it has `is`) → inline.
17
+ * - Otherwise → wrapped: keyword on its own line; items at +1 indent;
18
+ * bare items flow-fill ≤ LINE_BUDGET, comma-separated intra-line,
19
+ * no trailing commas; `is` items each on own line; annotated items
20
+ * each on own line, annotation on the line above.
21
+ */
22
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ var desc = Object.getOwnPropertyDescriptor(m, k);
25
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
26
+ desc = { enumerable: true, get: function() { return m[k]; } };
27
+ }
28
+ Object.defineProperty(o, k2, desc);
29
+ }) : (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ o[k2] = m[k];
32
+ }));
33
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
34
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
35
+ }) : function(o, v) {
36
+ o["default"] = v;
37
+ });
38
+ var __importStar = (this && this.__importStar) || (function () {
39
+ var ownKeys = function(o) {
40
+ ownKeys = Object.getOwnPropertyNames || function (o) {
41
+ var ar = [];
42
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
43
+ return ar;
44
+ };
45
+ return ownKeys(o);
46
+ };
47
+ return function (mod) {
48
+ if (mod && mod.__esModule) return mod;
49
+ var result = {};
50
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
51
+ __setModuleDefault(result, mod);
52
+ return result;
53
+ };
54
+ })();
55
+ Object.defineProperty(exports, "__esModule", { value: true });
56
+ exports.formatSectionStatement = formatSectionStatement;
57
+ const antlr4ts_1 = require("antlr4ts");
58
+ const tree_1 = require("antlr4ts/tree");
59
+ const parser = __importStar(require("../lib/Malloy/MalloyParser"));
60
+ const tokens_1 = require("./tokens");
61
+ const leaf_1 = require("./leaf");
62
+ const inline_renderer_1 = require("./inline-renderer");
63
+ function formatSectionStatement(f, stmt, rule) {
64
+ var _a;
65
+ const keywordTok = findKeyword(stmt, rule.keywordTypes);
66
+ const listCtx = rule.list(stmt);
67
+ if (!keywordTok || !listCtx) {
68
+ (0, leaf_1.formatTokenRange)(f, stmt._start.tokenIndex, stmt._stop.tokenIndex);
69
+ return;
70
+ }
71
+ for (let i = 0; i < stmt.childCount; i++) {
72
+ const c = stmt.getChild(i);
73
+ if (c instanceof tree_1.TerminalNode && c.symbol === keywordTok) {
74
+ (0, leaf_1.flushHiddenBefore)(f, keywordTok.tokenIndex);
75
+ if (f.o.indent > 0)
76
+ f.o.nl();
77
+ else
78
+ (0, leaf_1.startStatementLine)(f);
79
+ f.o.text(((_a = keywordTok.text) !== null && _a !== void 0 ? _a : '').replace(/\s+/g, ''));
80
+ (0, leaf_1.note)(f, keywordTok.type, keywordTok.tokenIndex, keywordTok);
81
+ continue;
82
+ }
83
+ if (c === listCtx) {
84
+ const items = listItems(listCtx, rule.itemKind);
85
+ if (items.length > 0)
86
+ formatSectionList(f, items);
87
+ continue;
88
+ }
89
+ f.format(c);
90
+ }
91
+ }
92
+ function findKeyword(node, types) {
93
+ for (let i = 0; i < node.childCount; i++) {
94
+ const c = node.getChild(i);
95
+ if (c instanceof tree_1.TerminalNode && types.includes(c.symbol.type))
96
+ return c.symbol;
97
+ }
98
+ return undefined;
99
+ }
100
+ function listItems(listCtx, itemKind) {
101
+ // Type predicate (`c is ParserRuleContext`) so callers don't need a cast.
102
+ const matches = (c) => {
103
+ if (!(c instanceof antlr4ts_1.ParserRuleContext))
104
+ return false;
105
+ switch (itemKind) {
106
+ case 'fieldEntry':
107
+ return c instanceof parser.QueryFieldEntryContext;
108
+ case 'nestEntry':
109
+ return c instanceof parser.NestEntryContext;
110
+ case 'fieldDef':
111
+ return c instanceof parser.FieldDefContext;
112
+ case 'fieldName':
113
+ return c instanceof parser.FieldNameContext;
114
+ case 'collectionMember':
115
+ return c instanceof parser.CollectionMemberContext;
116
+ case 'orderBySpec':
117
+ return c instanceof parser.OrderBySpecContext;
118
+ case 'fieldExpr':
119
+ return c instanceof parser.FieldExprContext;
120
+ }
121
+ };
122
+ const out = [];
123
+ for (let i = 0; i < listCtx.childCount; i++) {
124
+ const c = listCtx.getChild(i);
125
+ if (matches(c))
126
+ out.push(c);
127
+ }
128
+ return out;
129
+ }
130
+ function classifyItem(f, ctx) {
131
+ let hasIs = false;
132
+ let hasAnnotation = false;
133
+ for (let i = ctx._start.tokenIndex; i <= ctx._stop.tokenIndex; i++) {
134
+ const t = f.tokens[i];
135
+ if (t.type === tokens_1.L.IS)
136
+ hasIs = true;
137
+ if (t.type === tokens_1.L.ANNOTATION ||
138
+ t.type === tokens_1.L.DOC_ANNOTATION ||
139
+ t.type === tokens_1.L.BLOCK_ANNOTATION_BEGIN ||
140
+ t.type === tokens_1.L.DOC_BLOCK_ANNOTATION_BEGIN)
141
+ hasAnnotation = true;
142
+ }
143
+ return { ctx, hasIs, hasAnnotation };
144
+ }
145
+ function formatSectionList(f, items) {
146
+ const itemInfos = items.map(it => classifyItem(f, it));
147
+ const noAnnotations = itemInfos.every(info => !info.hasAnnotation);
148
+ const allBare = itemInfos.every(info => !info.hasIs && !info.hasAnnotation);
149
+ const firstItem = items[0];
150
+ const lastItem = items[items.length - 1];
151
+ // Inline candidate: no annotations, no hidden-channel comments anywhere in
152
+ // the items' span (renderItemInline drops them), AND either all bare or
153
+ // exactly one item.
154
+ const itemsHaveComments = (0, leaf_1.hasCommentsInRange)(f, firstItem._start.tokenIndex, lastItem._stop.tokenIndex);
155
+ const inlineEligible = noAnnotations && !itemsHaveComments && (allBare || items.length === 1);
156
+ if (inlineEligible) {
157
+ const renderedItems = itemInfos.map(info => (0, inline_renderer_1.renderItemInline)(f, info.ctx));
158
+ const inlineBody = renderedItems.join(', ');
159
+ const candidateLen = f.o.lineLengthSoFar() + 1 /* space */ + inlineBody.length;
160
+ if (candidateLen <= tokens_1.LINE_BUDGET) {
161
+ f.o.text(' ');
162
+ f.o.text(inlineBody);
163
+ (0, leaf_1.note)(f, lastItem._stop.type, lastItem._stop.tokenIndex, lastItem._stop);
164
+ return;
165
+ }
166
+ }
167
+ // Wrapped form. Two paths:
168
+ // - No comments anywhere: original flow-fill — bare items pack into lines
169
+ // at LINE_BUDGET, `is`/annotated items each get their own line.
170
+ // - Comments anywhere in the items' span: every item emits on its own
171
+ // line via f.format(), which goes through emitVisibleToken /
172
+ // flushHiddenBefore and handles trailing-vs-leading attachment for
173
+ // hidden-channel tokens correctly. Flow-fill density is sacrificed for
174
+ // comment correctness.
175
+ f.o.indent++;
176
+ f.o.nl();
177
+ if (itemsHaveComments) {
178
+ f.lastEmittedIdx = firstItem._start.tokenIndex - 1;
179
+ for (let k = 0; k < itemInfos.length; k++) {
180
+ const info = itemInfos[k];
181
+ // flushHiddenBefore handles between-item comments and advances
182
+ // lastEmittedIdx so they aren't re-emitted by f.format(item)'s own first
183
+ // emit.
184
+ (0, leaf_1.flushHiddenBefore)(f, info.ctx._start.tokenIndex);
185
+ f.o.nl();
186
+ f.format(info.ctx);
187
+ }
188
+ flushSameLineTail(f, lastItem._stop);
189
+ f.o.indent--;
190
+ // Update lastEmittedType for leading-action decisions, but DO NOT reset
191
+ // lastEmittedIdx — flushSameLineTail may have advanced it past tail
192
+ // comments, and rolling it back would let the parent re-emit them.
193
+ f.lastEmittedType = lastItem._stop.type;
194
+ return;
195
+ }
196
+ // No-comments fast path: flow-fill packing as before.
197
+ let curBare = '';
198
+ const flushBare = () => {
199
+ if (curBare.length > 0) {
200
+ f.o.text(curBare);
201
+ f.o.nl();
202
+ curBare = '';
203
+ }
204
+ };
205
+ for (const info of itemInfos) {
206
+ if (info.hasIs || info.hasAnnotation) {
207
+ flushBare();
208
+ (0, leaf_1.flushHiddenBefore)(f, info.ctx._start.tokenIndex);
209
+ f.format(info.ctx);
210
+ f.o.nl();
211
+ }
212
+ else {
213
+ const itemText = (0, inline_renderer_1.renderItemInline)(f, info.ctx);
214
+ const indentChars = f.o.indent * tokens_1.INDENT_STR.length;
215
+ const sepLen = curBare.length > 0 ? 2 : 0; // ", "
216
+ const projected = indentChars + curBare.length + sepLen + itemText.length;
217
+ if (curBare.length > 0 && projected > tokens_1.LINE_BUDGET) {
218
+ flushBare();
219
+ curBare = itemText;
220
+ }
221
+ else {
222
+ curBare = curBare.length > 0 ? curBare + ', ' + itemText : itemText;
223
+ }
224
+ }
225
+ }
226
+ flushBare();
227
+ flushSameLineTail(f, lastItem._stop);
228
+ f.o.indent--;
229
+ // See comment in the with-comments branch: keep the advanced
230
+ // lastEmittedIdx so tail comments aren't re-emitted by the parent.
231
+ f.lastEmittedType = lastItem._stop.type;
232
+ }
233
+ // After the last item of a wrapped section list, flush any trailing comments
234
+ // on the SAME source line as the last item — those are tail comments belong-
235
+ // ing to the last item and should emit at the section's inner indent.
236
+ // Different-line comments are leading comments for the next statement; leave
237
+ // them for the parent context to emit at the outer indent.
238
+ function flushSameLineTail(f, lastTok) {
239
+ const lastEndLine = (0, tokens_1.endLineOf)(lastTok);
240
+ let j = lastTok.tokenIndex + 1;
241
+ while (j < f.tokens.length) {
242
+ const t = f.tokens[j];
243
+ if (t.channel !== antlr4ts_1.Token.HIDDEN_CHANNEL)
244
+ break;
245
+ if (t.line !== lastEndLine)
246
+ break;
247
+ j++;
248
+ }
249
+ if (j > lastTok.tokenIndex + 1) {
250
+ // The wrapping loop emitted a per-item newline after the last item, but
251
+ // a same-line tail comment should attach to that item's line — not float
252
+ // on a fresh one. Drop the trailing newline so emitHiddenToken's
253
+ // same-line branch reattaches the comment correctly (and adds a trailing
254
+ // newline back for EOL comments). Without this, the comment lands on a
255
+ // new line, and a re-parse sees it as a different-line comment, breaking
256
+ // idempotence.
257
+ f.o.trimTrailingNewlines();
258
+ (0, leaf_1.flushHiddenBefore)(f, j);
259
+ }
260
+ }
261
+ //# sourceMappingURL=sections.js.map
@@ -0,0 +1,13 @@
1
+ import type { Token } from 'antlr4ts';
2
+ import { MalloyLexer } from '../lib/Malloy/MalloyLexer';
3
+ export declare const L: typeof MalloyLexer;
4
+ export declare const LINE_BUDGET = 100;
5
+ export declare const INDENT_STR = " ";
6
+ export declare const SECTION_TOKENS: Set<number>;
7
+ export declare const TOP_LEVEL_STARTERS: Set<number>;
8
+ export declare const CALL_HUG_AFTER: Set<number>;
9
+ export declare const BINARY_OPS: Set<number>;
10
+ export type LeadingAction = 'glue' | 'hug' | 'space';
11
+ export declare function leadingAction(prevType: number | null, nextType: number): LeadingAction;
12
+ export declare function endLineOf(t: Token): number;
13
+ export declare function findMatching(tokens: Token[], startIdx: number, beginType: number, endType: number): number;