Skip to content

Commit 8c17a77

Browse files
committed
finalize the generic printer library
1 parent 7d81d9c commit 8c17a77

15 files changed

Lines changed: 369 additions & 90 deletions

src/printer/__tests__/builders.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
* 2.0.
66
*/
77

8-
/*
9-
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
10-
* or more contributor license agreements. Licensed under the "Elastic License
11-
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
12-
* Public License v 1"; you may not use this file except in compliance with, at
13-
* your election, the "Elastic License 2.0", the "GNU Affero General Public
14-
* License v3.0 only", or the "Server Side Public License, v 1".
15-
*/
16-
178
import {
189
text,
1910
line,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { text, softline, hardline, group, indent, conditionalGroup, join, layout } from '..';
9+
10+
describe('conditionalGroup', () => {
11+
it('uses first state (flat) when it fits', () => {
12+
const items = [text('alpha'), text('beta'), text('gamma')];
13+
14+
const doc = conditionalGroup([
15+
// Phase 1: flat with spaces — "alpha, beta, gamma"
16+
group(join(text(', '), items)),
17+
// Phase 2: compact — "alpha,beta,gamma"
18+
group(join([text(','), softline], items)),
19+
// Phase 3: one per line
20+
group([indent([hardline, join([text(','), hardline], items)]), hardline], {
21+
shouldBreak: true,
22+
}),
23+
]);
24+
25+
expect(layout(doc, { printWidth: 20 })).toBe('alpha, beta, gamma');
26+
expect(layout(doc, { printWidth: 17 })).toBe('alpha,beta,gamma');
27+
expect(layout(doc, { printWidth: 10 })).toBe('\n alpha,\n beta,\n gamma\n');
28+
});
29+
30+
it('three-phase list with brackets', () => {
31+
const items = [text('alpha'), text('beta'), text('gamma'), text('delta')];
32+
const sep = text(',');
33+
34+
const doc = conditionalGroup([
35+
// Phase 1: all on one line — "(alpha, beta, gamma, delta)" = 27
36+
group([text('('), join([sep, text(' ')], items), text(')')]),
37+
// Phase 2: compact (no spaces) — "(alpha,beta,gamma,delta)" = 24
38+
group([text('('), join([sep, softline], items), text(')')]),
39+
// Phase 3: one per line
40+
group([text('('), indent([hardline, join([sep, hardline], items)]), hardline, text(')')], {
41+
shouldBreak: true,
42+
}),
43+
]);
44+
45+
expect(layout(doc, { printWidth: 30 })).toBe('(alpha, beta, gamma, delta)');
46+
expect(layout(doc, { printWidth: 25 })).toBe('(alpha,beta,gamma,delta)');
47+
expect(layout(doc, { printWidth: 10 })).toBe('(\n alpha,\n beta,\n gamma,\n delta\n)');
48+
});
49+
50+
it('with two states only', () => {
51+
const doc = conditionalGroup([
52+
// Try flat
53+
join([text(', ')], [text('aaa'), text('bbb'), text('ccc')]),
54+
// Fallback: broken
55+
group(
56+
[indent([hardline, join([text(','), hardline], [text('aaa'), text('bbb'), text('ccc')])])],
57+
{ shouldBreak: true }
58+
),
59+
]);
60+
61+
expect(layout(doc, { printWidth: 20 })).toBe('aaa, bbb, ccc');
62+
expect(layout(doc, { printWidth: 10 })).toBe('\n aaa,\n bbb,\n ccc');
63+
});
64+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { text, line, softline, fill, join, layout } from '..';
9+
10+
describe('fill (wrapping layout)', () => {
11+
it('renders all items on one line when they fit', () => {
12+
const items = ['one', 'two', 'three'].map(text);
13+
const doc = fill(join(line, items));
14+
15+
expect(layout(doc, { printWidth: 80 })).toBe('one two three');
16+
});
17+
18+
it('wraps items across lines', () => {
19+
const items = ['aaa', 'bbb', 'ccc', 'ddd', 'eee'].map(text);
20+
const doc = fill(join(line, items));
21+
22+
expect(layout(doc, { printWidth: 12 })).toBe('aaa bbb ccc\nddd eee');
23+
});
24+
25+
it('wraps with softline (no spaces)', () => {
26+
const items = ['aaa', 'bbb', 'ccc', 'ddd'].map(text);
27+
const doc = fill(join(softline, items));
28+
29+
expect(layout(doc, { printWidth: 10 })).toBe('aaabbbccc\nddd');
30+
});
31+
32+
it('handles single item', () => {
33+
const doc = fill([text('hello')]);
34+
35+
expect(layout(doc, { printWidth: 80 })).toBe('hello');
36+
});
37+
38+
it('handles two items', () => {
39+
const doc = fill([text('hello'), line, text('world')]);
40+
41+
expect(layout(doc, { printWidth: 80 })).toBe('hello world');
42+
expect(layout(doc, { printWidth: 5 })).toBe('hello\nworld');
43+
});
44+
45+
it('handles empty parts', () => {
46+
const doc = fill([]);
47+
48+
expect(layout(doc, { printWidth: 80 })).toBe('');
49+
});
50+
51+
it('wraps words example', () => {
52+
const words = 'the quick brown fox jumps over the lazy dog'.split(' ').map(text);
53+
const doc = fill(join(line, words));
54+
55+
expect(layout(doc, { printWidth: 20 })).toBe('the quick brown fox\njumps over the lazy\ndog');
56+
});
57+
58+
it('fill with width 15', () => {
59+
const items = ['one', 'two', 'three', 'four', 'five'].map(text);
60+
const doc = fill(join(line, items));
61+
62+
expect(layout(doc, { printWidth: 15 })).toBe('one two three\nfour five');
63+
});
64+
65+
it('fill with width 10', () => {
66+
const items = ['one', 'two', 'three', 'four', 'five'].map(text);
67+
const doc = fill(join(line, items));
68+
69+
expect(layout(doc, { printWidth: 10 })).toBe('one two\nthree four\nfive');
70+
});
71+
});
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { text, hardline, group, lineSuffix, lineSuffixBoundary, layout } from '..';
9+
10+
describe('lineSuffix', () => {
11+
it('places comment at end of line', () => {
12+
const doc = [text('x'), lineSuffix(text(' # comment')), text(';'), hardline, text('y')];
13+
14+
expect(layout(doc, { printWidth: 80 })).toBe('x; # comment\ny');
15+
});
16+
17+
it('buffers multiple suffixes', () => {
18+
const doc = [
19+
text('a'),
20+
lineSuffix(text(' // c1')),
21+
text('b'),
22+
lineSuffix(text(' // c2')),
23+
hardline,
24+
text('next'),
25+
];
26+
27+
expect(layout(doc, { printWidth: 80 })).toBe('ab // c1 // c2\nnext');
28+
});
29+
30+
it('flushes suffix at end of document', () => {
31+
const doc = [text('code'), lineSuffix(text(' // trailing'))];
32+
33+
expect(layout(doc, { printWidth: 80 })).toBe('code // trailing');
34+
});
35+
36+
it('suffix ordering with trailing punctuation', () => {
37+
const doc = [text('field'), lineSuffix(text(' // comment')), text(','), hardline, text('next')];
38+
39+
expect(layout(doc, { printWidth: 80 })).toBe('field, // comment\nnext');
40+
});
41+
});
42+
43+
describe('lineSuffixBoundary', () => {
44+
it('flushes pending suffix before boundary', () => {
45+
const doc = group([text('{'), lineSuffix(text(' # c')), lineSuffixBoundary, text('}')]);
46+
47+
expect(layout(doc, { printWidth: 80 })).toBe('{ # c\n}');
48+
});
49+
50+
it('does nothing when no suffix is pending', () => {
51+
const doc = [text('a'), lineSuffixBoundary, text('b')];
52+
53+
expect(layout(doc, { printWidth: 80 })).toBe('ab');
54+
});
55+
});
56+
57+
describe('inline block comments (text-based)', () => {
58+
it('left and right inline comments are plain text', () => {
59+
const doc = [text('/* left */'), text(' '), text('node'), text(' '), text('/* right */')];
60+
61+
expect(layout(doc, { printWidth: 80 })).toBe('/* left */ node /* right */');
62+
});
63+
});
64+
65+
describe('own-line comments', () => {
66+
it('top comment above node', () => {
67+
const doc = [text('// top comment'), hardline, text('node')];
68+
69+
expect(layout(doc, { printWidth: 80 })).toBe('// top comment\nnode');
70+
});
71+
72+
it('bottom comment below node', () => {
73+
const doc = [text('node'), hardline, text('// bottom comment')];
74+
75+
expect(layout(doc, { printWidth: 80 })).toBe('node\n// bottom comment');
76+
});
77+
78+
it('full 5-slot comment model', () => {
79+
const nodeDoc = group([text('('), text('value'), text(')')]);
80+
81+
const doc = [
82+
text('// top comment'),
83+
hardline,
84+
text('/* left */ '),
85+
nodeDoc,
86+
text(' /* right */'),
87+
lineSuffix(text(' // trailing')),
88+
hardline,
89+
text('// bottom comment'),
90+
];
91+
92+
expect(layout(doc, { printWidth: 80 })).toBe(
93+
'// top comment\n/* left */ (value) /* right */ // trailing\n// bottom comment'
94+
);
95+
});
96+
});

src/printer/__tests__/layout.scenarios.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
* 2.0.
66
*/
77

8-
/*
9-
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
10-
* or more contributor license agreements. Licensed under the "Elastic License
11-
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
12-
* Public License v 1"; you may not use this file except in compliance with, at
13-
* your election, the "Elastic License 2.0", the "GNU Affero General Public
14-
* License v3.0 only", or the "Server Side Public License, v 1".
15-
*/
16-
178
import { text, line, softline, hardline, group, indent, join, fill, layout } from '..';
189

1910
describe('layout scenarios', () => {

src/printer/__tests__/layout.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
* 2.0.
66
*/
77

8-
/*
9-
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
10-
* or more contributor license agreements. Licensed under the "Elastic License
11-
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
12-
* Public License v 1"; you may not use this file except in compliance with, at
13-
* your election, the "Elastic License 2.0", the "GNU Affero General Public
14-
* License v3.0 only", or the "Server Side Public License, v 1".
15-
*/
16-
178
import {
189
text,
1910
line,

src/printer/__tests__/prepare.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
* 2.0.
66
*/
77

8-
/*
9-
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
10-
* or more contributor license agreements. Licensed under the "Elastic License
11-
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
12-
* Public License v 1"; you may not use this file except in compliance with, at
13-
* your election, the "Elastic License 2.0", the "GNU Affero General Public
14-
* License v3.0 only", or the "Server Side Public License, v 1".
15-
*/
16-
178
import { text, line, hardline } from '../builders';
189
import { propagateBreaks } from '../prepare';
1910
import type { GroupNode } from '../types';

0 commit comments

Comments
 (0)