@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.
- package/dist/api/foundation/config_resolve.js +12 -20
- package/dist/internal.d.ts +2 -0
- package/dist/internal.js +3 -1
- package/dist/lang/prettify/binary-chain.d.ts +3 -0
- package/dist/lang/prettify/binary-chain.js +65 -0
- package/dist/lang/prettify/block-body.d.ts +4 -0
- package/dist/lang/prettify/block-body.js +74 -0
- package/dist/lang/prettify/error-listener.d.ts +6 -0
- package/dist/lang/prettify/error-listener.js +19 -0
- package/dist/lang/prettify/field-properties.d.ts +3 -0
- package/dist/lang/prettify/field-properties.js +57 -0
- package/dist/lang/prettify/formatter.d.ts +17 -0
- package/dist/lang/prettify/formatter.js +145 -0
- package/dist/lang/prettify/index.d.ts +19 -0
- package/dist/lang/prettify/index.js +139 -0
- package/dist/lang/prettify/inline-renderer.d.ts +3 -0
- package/dist/lang/prettify/inline-renderer.js +101 -0
- package/dist/lang/prettify/leaf.d.ts +9 -0
- package/dist/lang/prettify/leaf.js +313 -0
- package/dist/lang/prettify/out.d.ts +12 -0
- package/dist/lang/prettify/out.js +74 -0
- package/dist/lang/prettify/pick-case.d.ts +5 -0
- package/dist/lang/prettify/pick-case.js +222 -0
- package/dist/lang/prettify/rules.d.ts +13 -0
- package/dist/lang/prettify/rules.js +103 -0
- package/dist/lang/prettify/sections.d.ts +4 -0
- package/dist/lang/prettify/sections.js +261 -0
- package/dist/lang/prettify/tokens.d.ts +13 -0
- package/dist/lang/prettify/tokens.js +160 -0
- package/dist/lang/prettify/types.d.ts +9 -0
- package/dist/lang/prettify/types.js +7 -0
- package/dist/lang/run-malloy-parser.d.ts +23 -0
- package/dist/lang/run-malloy-parser.js +32 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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,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;
|