@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,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* renderItemInline — flat-string form of a parse-rule's token range.
|
|
7
|
+
*
|
|
8
|
+
* Used by:
|
|
9
|
+
* - section-list inline measurement and bare-item flow-fill
|
|
10
|
+
* - postfix `{…}` inline form
|
|
11
|
+
* - pick / case alignment (rendering values and conditions to strings)
|
|
12
|
+
*
|
|
13
|
+
* Inter-token spacing comes from `leadingAction` in ./tokens, the same
|
|
14
|
+
* classifier the leaf walker (./leaf) consults. Both walkers therefore agree
|
|
15
|
+
* on what spacing goes between any two adjacent tokens; inline measurement
|
|
16
|
+
* predicts actual emission.
|
|
17
|
+
*
|
|
18
|
+
* Walker-specific divergence:
|
|
19
|
+
* - This produces a flat string (no newlines, no indentation).
|
|
20
|
+
* - SEMI emits `; ` (compact-inline form), not a newline.
|
|
21
|
+
* - COMMA emits `, ` (intra-line form), not a newline.
|
|
22
|
+
* - The space-coalescing skip-list omits `\n` (we never emit one) but is
|
|
23
|
+
* otherwise the same as Out.space.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.renderItemInline = renderItemInline;
|
|
27
|
+
const antlr4ts_1 = require("antlr4ts");
|
|
28
|
+
const tokens_1 = require("./tokens");
|
|
29
|
+
function renderItemInline(f, ctx) {
|
|
30
|
+
var _a;
|
|
31
|
+
let buf = '';
|
|
32
|
+
let lastType = null;
|
|
33
|
+
// Coalescing space: skip if buffer is empty or last char already provides
|
|
34
|
+
// separation. Mirrors Out.space() (./out) minus the newline check, since
|
|
35
|
+
// this renderer never emits a newline.
|
|
36
|
+
const space = () => {
|
|
37
|
+
if (buf.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
const last = buf[buf.length - 1];
|
|
40
|
+
if (last === ' ' || last === '(' || last === '[' || last === '.')
|
|
41
|
+
return;
|
|
42
|
+
buf += ' ';
|
|
43
|
+
};
|
|
44
|
+
const trim = () => {
|
|
45
|
+
buf = buf.replace(/ +$/, '');
|
|
46
|
+
};
|
|
47
|
+
for (let i = ctx._start.tokenIndex; i <= ctx._stop.tokenIndex; i++) {
|
|
48
|
+
const t = f.tokens[i];
|
|
49
|
+
if (t.channel === antlr4ts_1.Token.HIDDEN_CHANNEL)
|
|
50
|
+
continue;
|
|
51
|
+
const text = (_a = t.text) !== null && _a !== void 0 ? _a : '';
|
|
52
|
+
if (t.type === tokens_1.L.SQL_BEGIN) {
|
|
53
|
+
const endIdx = (0, tokens_1.findMatching)(f.tokens, i, tokens_1.L.SQL_BEGIN, tokens_1.L.SQL_END);
|
|
54
|
+
const stop = f.tokens[endIdx].stopIndex;
|
|
55
|
+
space();
|
|
56
|
+
buf += f.src.substring(t.startIndex, stop + 1);
|
|
57
|
+
i = endIdx;
|
|
58
|
+
lastType = tokens_1.L.SQL_END;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (t.type === tokens_1.L.CCURLY) {
|
|
62
|
+
// `{}` empty: no inner space. `{ x }`: both inner spaces.
|
|
63
|
+
if (buf.length > 0 && !buf.endsWith('{') && !buf.endsWith(' '))
|
|
64
|
+
buf += ' ';
|
|
65
|
+
else
|
|
66
|
+
trim();
|
|
67
|
+
buf += '}';
|
|
68
|
+
lastType = t.type;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// Walker-specific divergence: COMMA and SEMI use compact-inline form
|
|
72
|
+
// (`, ` and `; `). The leaf walker (./leaf) emits newlines for these in
|
|
73
|
+
// wrapped contexts.
|
|
74
|
+
if (t.type === tokens_1.L.COMMA) {
|
|
75
|
+
trim();
|
|
76
|
+
buf += ', ';
|
|
77
|
+
lastType = t.type;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (t.type === tokens_1.L.SEMI) {
|
|
81
|
+
trim();
|
|
82
|
+
buf += '; ';
|
|
83
|
+
lastType = t.type;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// Everything else: classifier-driven leading separator + text + (if a
|
|
87
|
+
// binary op) trailing space. Same shape as the leaf walker's default.
|
|
88
|
+
const action = (0, tokens_1.leadingAction)(lastType, t.type);
|
|
89
|
+
if (action === 'glue')
|
|
90
|
+
trim();
|
|
91
|
+
else if (action === 'space')
|
|
92
|
+
space();
|
|
93
|
+
// 'hug' — emit nothing before
|
|
94
|
+
buf += text;
|
|
95
|
+
if (tokens_1.BINARY_OPS.has(t.type))
|
|
96
|
+
buf += ' ';
|
|
97
|
+
lastType = t.type;
|
|
98
|
+
}
|
|
99
|
+
return buf;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=inline-renderer.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Token } from 'antlr4ts';
|
|
2
|
+
import type { Formatter } from './formatter';
|
|
3
|
+
export declare function note(f: Formatter, tokenType: number, idx: number, endTok: Token): void;
|
|
4
|
+
export declare function flushHiddenBefore(f: Formatter, idx: number): void;
|
|
5
|
+
export declare function startStatementLine(f: Formatter): void;
|
|
6
|
+
export declare function approxInlineSpan(f: Formatter, fromIdx: number, toIdx: number): number;
|
|
7
|
+
export declare function hasCommentsInRange(f: Formatter, fromIdx: number, toIdx: number): boolean;
|
|
8
|
+
export declare function emitVisibleToken(f: Formatter, t: Token, idx: number): void;
|
|
9
|
+
export declare function formatTokenRange(f: Formatter, fromIdx: number, toIdx: number): void;
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* Leaf walker: per-token spacing, structural punctuation, comment placement,
|
|
7
|
+
* and the small helpers the rule formatters use to look at adjacent tokens.
|
|
8
|
+
*
|
|
9
|
+
* Per-rule formatters call into here via:
|
|
10
|
+
* - emitVisibleToken — the leaf
|
|
11
|
+
* - flushHiddenBefore — emit pending comments before a target index
|
|
12
|
+
* - note — atomic per-token state update
|
|
13
|
+
* - startStatementLine — newline (consuming `needBlank`) for stmt start
|
|
14
|
+
* - approxInlineSpan — line-budget overestimate for a token range
|
|
15
|
+
* - hasCommentsInRange — comment-presence check (gates inline form)
|
|
16
|
+
* - formatTokenRange — emit a span via the leaf walker
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.note = note;
|
|
20
|
+
exports.flushHiddenBefore = flushHiddenBefore;
|
|
21
|
+
exports.startStatementLine = startStatementLine;
|
|
22
|
+
exports.approxInlineSpan = approxInlineSpan;
|
|
23
|
+
exports.hasCommentsInRange = hasCommentsInRange;
|
|
24
|
+
exports.emitVisibleToken = emitVisibleToken;
|
|
25
|
+
exports.formatTokenRange = formatTokenRange;
|
|
26
|
+
const antlr4ts_1 = require("antlr4ts");
|
|
27
|
+
const tokens_1 = require("./tokens");
|
|
28
|
+
// Update per-token state after emitting a token (or a token-like span).
|
|
29
|
+
function note(f, tokenType, idx, endTok) {
|
|
30
|
+
f.lastEmittedType = tokenType;
|
|
31
|
+
f.prevTokenEndLine = (0, tokens_1.endLineOf)(endTok);
|
|
32
|
+
f.lastEmittedIdx = idx;
|
|
33
|
+
}
|
|
34
|
+
// Emit any hidden-channel tokens (comments) sitting between f.lastEmittedIdx
|
|
35
|
+
// and idx-1, advancing f.lastEmittedIdx so they are not re-emitted by a later
|
|
36
|
+
// call. Visible tokens in the same range (e.g. commas the section-list rule
|
|
37
|
+
// chose to drop) are skipped.
|
|
38
|
+
function flushHiddenBefore(f, idx) {
|
|
39
|
+
if (idx <= f.lastEmittedIdx + 1)
|
|
40
|
+
return;
|
|
41
|
+
for (let j = f.lastEmittedIdx + 1; j < idx; j++) {
|
|
42
|
+
const t = f.tokens[j];
|
|
43
|
+
if (t.channel === antlr4ts_1.Token.HIDDEN_CHANNEL) {
|
|
44
|
+
emitHiddenToken(f, t);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
f.lastEmittedIdx = idx - 1;
|
|
48
|
+
}
|
|
49
|
+
// Hidden-channel tokens (comments). Trailing comments (same line as previous
|
|
50
|
+
// token) attach with a space; leading comments (own line) start a fresh line.
|
|
51
|
+
function emitHiddenToken(f, t) {
|
|
52
|
+
var _a;
|
|
53
|
+
const text = (_a = t.text) !== null && _a !== void 0 ? _a : '';
|
|
54
|
+
const sameLine = f.prevTokenEndLine !== 0 && t.line === f.prevTokenEndLine;
|
|
55
|
+
if (t.type === tokens_1.L.COMMENT_TO_EOL) {
|
|
56
|
+
if (sameLine) {
|
|
57
|
+
f.o.space();
|
|
58
|
+
f.o.text(text.replace(/\s+$/, ''));
|
|
59
|
+
f.o.nl();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (f.o.indent === 0)
|
|
63
|
+
startStatementLine(f);
|
|
64
|
+
else
|
|
65
|
+
f.o.nl();
|
|
66
|
+
f.o.text(text.replace(/\s+$/, ''));
|
|
67
|
+
f.o.nl();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (t.type === tokens_1.L.BLOCK_COMMENT) {
|
|
71
|
+
if (sameLine) {
|
|
72
|
+
f.o.space();
|
|
73
|
+
f.o.text(text);
|
|
74
|
+
f.o.space();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
if (f.o.indent === 0)
|
|
78
|
+
startStatementLine(f);
|
|
79
|
+
else
|
|
80
|
+
f.o.nl();
|
|
81
|
+
f.o.text(text);
|
|
82
|
+
f.o.nl();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
f.prevTokenEndLine = (0, tokens_1.endLineOf)(t);
|
|
86
|
+
}
|
|
87
|
+
// Either consume `needBlank` (emit blank line) or just newline.
|
|
88
|
+
function startStatementLine(f) {
|
|
89
|
+
if (f.needBlank) {
|
|
90
|
+
f.o.blank();
|
|
91
|
+
f.needBlank = false;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
f.o.nl();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Approximate length of the inline form of tokens [fromIdx, toIdx]: sum of
|
|
98
|
+
// visible token text + 1 char between adjacent tokens. Used for budget checks
|
|
99
|
+
// (paren wrap, pickStatement wrap, etc.). It's an overestimate for dotted-paths
|
|
100
|
+
// and similar (`a.b` is 3 chars, our estimate is 5) but the direction is
|
|
101
|
+
// conservative — we wrap a touch sooner than strictly needed.
|
|
102
|
+
function approxInlineSpan(f, fromIdx, toIdx) {
|
|
103
|
+
var _a;
|
|
104
|
+
let len = 0;
|
|
105
|
+
let prev = -1;
|
|
106
|
+
for (let i = fromIdx; i <= toIdx; i++) {
|
|
107
|
+
const t = f.tokens[i];
|
|
108
|
+
if (t.channel === antlr4ts_1.Token.HIDDEN_CHANNEL)
|
|
109
|
+
continue;
|
|
110
|
+
if (t.type === antlr4ts_1.Token.EOF)
|
|
111
|
+
continue;
|
|
112
|
+
if (prev >= 0)
|
|
113
|
+
len += 1;
|
|
114
|
+
len += ((_a = t.text) !== null && _a !== void 0 ? _a : '').length;
|
|
115
|
+
prev = i;
|
|
116
|
+
}
|
|
117
|
+
return len;
|
|
118
|
+
}
|
|
119
|
+
// Are there any hidden-channel tokens (comments) in [fromIdx, toIdx]? Used to
|
|
120
|
+
// gate inline-form candidates: any path that goes through `renderItemInline`
|
|
121
|
+
// strips comments, so candidates with comments must fall back to wrapped or
|
|
122
|
+
// broken form where the leaf walker preserves them.
|
|
123
|
+
function hasCommentsInRange(f, fromIdx, toIdx) {
|
|
124
|
+
for (let i = fromIdx; i <= toIdx; i++) {
|
|
125
|
+
if (f.tokens[i].channel === antlr4ts_1.Token.HIDDEN_CHANNEL)
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
// Does the paren-pair at [openIdx, closeIdx] have any COMMA at its own depth?
|
|
131
|
+
// (Used to distinguish "function call with multiple args" from "single-arg
|
|
132
|
+
// call" / "empty parens".)
|
|
133
|
+
function hasCommaAtDepth1(f, openIdx, closeIdx) {
|
|
134
|
+
let depth = 0;
|
|
135
|
+
for (let i = openIdx + 1; i < closeIdx; i++) {
|
|
136
|
+
const t = f.tokens[i];
|
|
137
|
+
if (t.channel === antlr4ts_1.Token.HIDDEN_CHANNEL)
|
|
138
|
+
continue;
|
|
139
|
+
if (t.type === tokens_1.L.OPAREN || t.type === tokens_1.L.OBRACK || t.type === tokens_1.L.OCURLY)
|
|
140
|
+
depth++;
|
|
141
|
+
else if (t.type === tokens_1.L.CPAREN || t.type === tokens_1.L.CBRACK || t.type === tokens_1.L.CCURLY)
|
|
142
|
+
depth--;
|
|
143
|
+
else if (t.type === tokens_1.L.COMMA && depth === 0)
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
// The big switch. Each branch ends with `note(...)`.
|
|
149
|
+
function emitVisibleToken(f, t, idx) {
|
|
150
|
+
var _a, _b;
|
|
151
|
+
if (idx <= f.lastEmittedIdx)
|
|
152
|
+
return; // already emitted (e.g. SQL block range)
|
|
153
|
+
flushHiddenBefore(f, idx);
|
|
154
|
+
const text = (_a = t.text) !== null && _a !== void 0 ? _a : '';
|
|
155
|
+
// ---- Verbatim regions: SQL strings and block annotations ----
|
|
156
|
+
// We don't own a SQL formatter. Annotation indentation is significant.
|
|
157
|
+
if (t.type === tokens_1.L.SQL_BEGIN) {
|
|
158
|
+
const endIdx = (0, tokens_1.findMatching)(f.tokens, idx, tokens_1.L.SQL_BEGIN, tokens_1.L.SQL_END);
|
|
159
|
+
const stop = f.tokens[endIdx].stopIndex;
|
|
160
|
+
f.o.space();
|
|
161
|
+
f.o.text(f.src.substring(t.startIndex, stop + 1));
|
|
162
|
+
note(f, tokens_1.L.SQL_END, endIdx, f.tokens[endIdx]);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (t.type === tokens_1.L.BLOCK_ANNOTATION_BEGIN ||
|
|
166
|
+
t.type === tokens_1.L.DOC_BLOCK_ANNOTATION_BEGIN) {
|
|
167
|
+
const endIdx = (0, tokens_1.findMatching)(f.tokens, idx, t.type, tokens_1.L.BLOCK_ANNOTATION_END);
|
|
168
|
+
const stop = f.tokens[endIdx].stopIndex;
|
|
169
|
+
if (f.o.indent === 0)
|
|
170
|
+
startStatementLine(f);
|
|
171
|
+
else
|
|
172
|
+
f.o.nl();
|
|
173
|
+
f.o.text(f.src.substring(t.startIndex, stop + 1));
|
|
174
|
+
f.o.nl();
|
|
175
|
+
note(f, tokens_1.L.BLOCK_ANNOTATION_END, endIdx, f.tokens[endIdx]);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// ---- Single-line annotations on their own line ----
|
|
179
|
+
if (t.type === tokens_1.L.ANNOTATION || t.type === tokens_1.L.DOC_ANNOTATION) {
|
|
180
|
+
if (f.o.indent === 0)
|
|
181
|
+
startStatementLine(f);
|
|
182
|
+
else
|
|
183
|
+
f.o.nl();
|
|
184
|
+
f.o.text(text.replace(/\s+$/, ''));
|
|
185
|
+
f.o.nl();
|
|
186
|
+
note(f, t.type, idx, t);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// ---- Curly braces: indent in/out around block bodies ----
|
|
190
|
+
if (t.type === tokens_1.L.OCURLY) {
|
|
191
|
+
f.o.space();
|
|
192
|
+
f.o.text('{');
|
|
193
|
+
f.o.indent++;
|
|
194
|
+
f.o.nl();
|
|
195
|
+
note(f, t.type, idx, t);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (t.type === tokens_1.L.CCURLY) {
|
|
199
|
+
f.o.indent = Math.max(0, f.o.indent - 1);
|
|
200
|
+
f.o.nl();
|
|
201
|
+
f.o.text('}');
|
|
202
|
+
if (f.o.indent === 0)
|
|
203
|
+
f.needBlank = true;
|
|
204
|
+
note(f, t.type, idx, t);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// ---- Statement separators ----
|
|
208
|
+
// `;` in wrapped form is dropped — newlines do the job. (Inline `;` appears
|
|
209
|
+
// via renderItemInline, not here.)
|
|
210
|
+
if (t.type === tokens_1.L.SEMI) {
|
|
211
|
+
f.o.trimTrailingSpace();
|
|
212
|
+
f.o.nl();
|
|
213
|
+
if (f.o.indent === 0)
|
|
214
|
+
f.needBlank = true;
|
|
215
|
+
note(f, t.type, idx, t);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// ---- Commas ----
|
|
219
|
+
// At top level (parenDepth==0) → newline. Inside parens that the wrap logic
|
|
220
|
+
// flagged as multi-line → newline. Otherwise inline (just space).
|
|
221
|
+
if (t.type === tokens_1.L.COMMA) {
|
|
222
|
+
f.o.trimTrailingSpace();
|
|
223
|
+
f.o.text(',');
|
|
224
|
+
const innerBreaks = f.parenBreaks.length > 0 && f.parenBreaks[f.parenBreaks.length - 1];
|
|
225
|
+
if (f.parenDepth === 0 || innerBreaks)
|
|
226
|
+
f.o.nl();
|
|
227
|
+
note(f, t.type, idx, t);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// ---- Open paren / bracket: decide call-hug vs grouping, decide wrap ----
|
|
231
|
+
if (t.type === tokens_1.L.OPAREN || t.type === tokens_1.L.OBRACK) {
|
|
232
|
+
const action = (0, tokens_1.leadingAction)(f.lastEmittedType, t.type);
|
|
233
|
+
if (action === 'space')
|
|
234
|
+
f.o.space();
|
|
235
|
+
f.o.text(text);
|
|
236
|
+
// Decide whether the contents will exceed the line budget when laid out
|
|
237
|
+
// inline. Break only if there's somewhere useful to break:
|
|
238
|
+
// - call/subscript parens (action='hug'): must have ≥ 2 args (commas at
|
|
239
|
+
// this depth);
|
|
240
|
+
// - grouping parens (action='space'): any overflow — content's own
|
|
241
|
+
// rules will wrap.
|
|
242
|
+
const closeType = t.type === tokens_1.L.OPAREN ? tokens_1.L.CPAREN : tokens_1.L.CBRACK;
|
|
243
|
+
const matchIdx = (0, tokens_1.findMatching)(f.tokens, idx, t.type, closeType);
|
|
244
|
+
const inlineLen = approxInlineSpan(f, idx, matchIdx);
|
|
245
|
+
const wouldOverflow = f.o.lineLengthSoFar() + inlineLen > tokens_1.LINE_BUDGET;
|
|
246
|
+
const hasArgCommas = hasCommaAtDepth1(f, idx, matchIdx);
|
|
247
|
+
const isCall = action === 'hug';
|
|
248
|
+
const willBreak = wouldOverflow && (hasArgCommas || !isCall);
|
|
249
|
+
f.parenBreaks.push(willBreak);
|
|
250
|
+
f.parenDepth++;
|
|
251
|
+
if (willBreak) {
|
|
252
|
+
f.o.indent++;
|
|
253
|
+
f.o.nl();
|
|
254
|
+
}
|
|
255
|
+
note(f, t.type, idx, t);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (t.type === tokens_1.L.CPAREN || t.type === tokens_1.L.CBRACK) {
|
|
259
|
+
const wasBreak = (_b = f.parenBreaks.pop()) !== null && _b !== void 0 ? _b : false;
|
|
260
|
+
if (wasBreak) {
|
|
261
|
+
f.o.indent = Math.max(0, f.o.indent - 1);
|
|
262
|
+
f.o.nl();
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
f.o.trimTrailingSpace();
|
|
266
|
+
}
|
|
267
|
+
f.o.text(text);
|
|
268
|
+
f.parenDepth = Math.max(0, f.parenDepth - 1);
|
|
269
|
+
note(f, t.type, idx, t);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// ---- Section keyword fallback (no explicit handler took it) ----
|
|
273
|
+
// Inside a brace block, force a fresh line. Keeps v1-style readable output
|
|
274
|
+
// even for sections we don't yet handle.
|
|
275
|
+
if (tokens_1.SECTION_TOKENS.has(t.type) && f.o.indent > 0) {
|
|
276
|
+
f.o.nl();
|
|
277
|
+
f.o.text(text.replace(/\s+/g, ''));
|
|
278
|
+
note(f, t.type, idx, t);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// ---- Top-level statement starter ----
|
|
282
|
+
if (tokens_1.TOP_LEVEL_STARTERS.has(t.type) &&
|
|
283
|
+
f.o.indent === 0 &&
|
|
284
|
+
f.parenDepth === 0) {
|
|
285
|
+
startStatementLine(f);
|
|
286
|
+
f.o.text(text.replace(/\s+/g, ''));
|
|
287
|
+
note(f, t.type, idx, t);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// ---- Default: identifier / literal / keyword / DOT / COLON / TRIPLECOLON
|
|
291
|
+
// / binary op. Leading separator from the classifier; binary ops also
|
|
292
|
+
// get a trailing space.
|
|
293
|
+
const action = (0, tokens_1.leadingAction)(f.lastEmittedType, t.type);
|
|
294
|
+
if (action === 'glue')
|
|
295
|
+
f.o.trimTrailingSpace();
|
|
296
|
+
else if (action === 'space')
|
|
297
|
+
f.o.space();
|
|
298
|
+
// 'hug' — emit nothing before
|
|
299
|
+
f.o.text(text);
|
|
300
|
+
if (tokens_1.BINARY_OPS.has(t.type))
|
|
301
|
+
f.o.space();
|
|
302
|
+
note(f, t.type, idx, t);
|
|
303
|
+
}
|
|
304
|
+
// Emit each visible token in [fromIdx, toIdx] via the leaf walker.
|
|
305
|
+
function formatTokenRange(f, fromIdx, toIdx) {
|
|
306
|
+
for (let i = fromIdx; i <= toIdx; i++) {
|
|
307
|
+
const t = f.tokens[i];
|
|
308
|
+
if (t.channel !== antlr4ts_1.Token.HIDDEN_CHANNEL && t.type !== antlr4ts_1.Token.EOF) {
|
|
309
|
+
emitVisibleToken(f, t, i);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=leaf.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.Out = void 0;
|
|
8
|
+
const tokens_1 = require("./tokens");
|
|
9
|
+
// Append-only buffer with helpers for indentation, single-space coalescing,
|
|
10
|
+
// and newlines. Knows nothing about Malloy; the formatting rules call into it.
|
|
11
|
+
class Out {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.buf = '';
|
|
14
|
+
this.indent = 0;
|
|
15
|
+
}
|
|
16
|
+
// Append text. If the buffer ended with a newline, prepend the current
|
|
17
|
+
// indent first so the new text starts at the right column.
|
|
18
|
+
text(s) {
|
|
19
|
+
if (this.buf.endsWith('\n'))
|
|
20
|
+
this.buf += tokens_1.INDENT_STR.repeat(this.indent);
|
|
21
|
+
this.buf += s;
|
|
22
|
+
}
|
|
23
|
+
// Append at most one space. No-op at start of buffer, after newline, or
|
|
24
|
+
// after `(`, `[`, `.` (so `f(x` stays glued).
|
|
25
|
+
space() {
|
|
26
|
+
if (this.buf.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
const last = this.buf[this.buf.length - 1];
|
|
29
|
+
if (last === ' ' ||
|
|
30
|
+
last === '\n' ||
|
|
31
|
+
last === '(' ||
|
|
32
|
+
last === '[' ||
|
|
33
|
+
last === '.')
|
|
34
|
+
return;
|
|
35
|
+
this.buf += ' ';
|
|
36
|
+
}
|
|
37
|
+
// Force the next emit onto a new line. Trailing spaces are stripped.
|
|
38
|
+
nl() {
|
|
39
|
+
if (this.buf.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
this.buf = this.buf.replace(/ +$/, '');
|
|
42
|
+
if (!this.buf.endsWith('\n'))
|
|
43
|
+
this.buf += '\n';
|
|
44
|
+
}
|
|
45
|
+
// Force a blank line before the next emit. No-op at start of buffer.
|
|
46
|
+
blank() {
|
|
47
|
+
if (this.buf.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
this.nl();
|
|
50
|
+
if (!this.buf.endsWith('\n\n'))
|
|
51
|
+
this.buf += '\n';
|
|
52
|
+
}
|
|
53
|
+
trimTrailingSpace() {
|
|
54
|
+
this.buf = this.buf.replace(/ +$/, '');
|
|
55
|
+
}
|
|
56
|
+
trimTrailingNewlines() {
|
|
57
|
+
this.buf = this.buf.replace(/\n+$/, '');
|
|
58
|
+
}
|
|
59
|
+
// The column the next emit will land at. When the buffer ends with a
|
|
60
|
+
// newline the indent isn't yet in `buf`, so we have to add the pending
|
|
61
|
+
// indent width to predict where the next text will appear.
|
|
62
|
+
lineLengthSoFar() {
|
|
63
|
+
if (this.buf.length === 0 || this.buf.endsWith('\n')) {
|
|
64
|
+
return this.indent * tokens_1.INDENT_STR.length;
|
|
65
|
+
}
|
|
66
|
+
const lastNl = this.buf.lastIndexOf('\n');
|
|
67
|
+
return this.buf.length - (lastNl + 1);
|
|
68
|
+
}
|
|
69
|
+
toString() {
|
|
70
|
+
return this.buf.replace(/\n*$/, '\n');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.Out = Out;
|
|
74
|
+
//# sourceMappingURL=out.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as parser from '../lib/Malloy/MalloyParser';
|
|
2
|
+
import type { Formatter } from './formatter';
|
|
3
|
+
export declare function formatPickStatement(f: Formatter, ctx: parser.PickStatementContext): void;
|
|
4
|
+
export declare function formatPick(f: Formatter, ctx: parser.PickContext): void;
|
|
5
|
+
export declare function formatCaseStatement(f: Formatter, ctx: parser.CaseStatementContext): void;
|