links-notation 0.30.1 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -0
- package/README.ru.md +10 -0
- package/dist/index.js +34 -5
- package/index.d.ts +8 -1
- package/package.json +1 -1
- package/src/Link.js +2 -5
- package/src/NestedFormat.js +55 -0
- package/src/Parser.js +13 -3
- package/src/index.js +2 -0
- package/tests/IndentedIdGroups.test.js +69 -0
- package/tests/NestedFormat.test.js +82 -0
- package/tests/SharedTextParity.test.js +1 -1
- package/tests/SyntaxMetadata.test.js +1 -1
package/README.md
CHANGED
|
@@ -215,6 +215,40 @@ console.log(group.children[0].children[0].element.getValuesString()); // Detail
|
|
|
215
215
|
`Parser.parse()` continues to return the existing list of links and expanded
|
|
216
216
|
paths. `parseGroups()` preserves indentation even for the `name:` syntax.
|
|
217
217
|
|
|
218
|
+
#### Indented ID Groups
|
|
219
|
+
|
|
220
|
+
`parse` defines the canonical link values for `x:\n a\n b`: `(x: a b)`.
|
|
221
|
+
`parseGroups` keeps the `x:` element as a self reference (`id: 'x'`,
|
|
222
|
+
`values: []`), with children separate from that empty value list. Leaf lines
|
|
223
|
+
containing a single reference inside an indented ID's value subtree become
|
|
224
|
+
references, so folding that subtree agrees with `parse`:
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
import { Link, Parser } from 'links-notation';
|
|
228
|
+
|
|
229
|
+
const parser = new Parser();
|
|
230
|
+
const unindent = ({ element, children }) =>
|
|
231
|
+
children.length
|
|
232
|
+
? new Link(element.id, [...element.values, ...children.map(unindent)])
|
|
233
|
+
: element;
|
|
234
|
+
const text = 'x:\n a\n b';
|
|
235
|
+
console.log(parser.parse(text)[0].format()); // (x: a b)
|
|
236
|
+
console.log(parser.parseGroups(text).map(unindent)[0].format()); // (x: a b)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Parentheses keep their meaning: `x:\n (a)` gives `(x: (a))` in both readings.
|
|
240
|
+
Named and multi-value children keep their structure too. Under an ordinary
|
|
241
|
+
head such as `x: kind`, the child `a` remains a one-value link `(a)`;
|
|
242
|
+
`parse` expands those lines into paths, so this folding function is only an
|
|
243
|
+
equivalence with `parse` for indented ID subtrees. A standalone `id:` or
|
|
244
|
+
`(id:)` has no values in either API. The Link model represents it with
|
|
245
|
+
`id: 'id', values: []`; its existing default formatter writes `(id)`.
|
|
246
|
+
|
|
247
|
+
Before 0.30.2, `parseGroups` kept the anonymous one-value wrapper around leaf
|
|
248
|
+
references under `x:`. Callers that accessed `child.element.values[0]` there
|
|
249
|
+
should now use `child.element`. C++ (`parse_groups`), Swift and Kotlin expose
|
|
250
|
+
the same group contract; the other six bindings do not expose this parser API.
|
|
251
|
+
|
|
218
252
|
`escapeReference` and `unescapeReference` are exported functions and static
|
|
219
253
|
methods on `Link`. Use minimal mode only for whole text lines; default escaping
|
|
220
254
|
still quotes spaces so references can be used inside tuples:
|
|
@@ -626,3 +660,48 @@ fail the format check.
|
|
|
626
660
|
|
|
627
661
|
Exact Unicode identifiers and values round-trip with the [reference literal contract](../docs/protocol/reference-literals.md)
|
|
628
662
|
introduced in 0.24.0, including mixed quotes, empty strings, controls and multiline text.
|
|
663
|
+
|
|
664
|
+
### Recursive nested output
|
|
665
|
+
|
|
666
|
+
`formatNested` writes a link's leading atoms on its line and its trailing link
|
|
667
|
+
values on indented lines, recursively:
|
|
668
|
+
|
|
669
|
+
```javascript
|
|
670
|
+
import { Link, Parser, formatNested } from 'links-notation';
|
|
671
|
+
const parser = new Parser();
|
|
672
|
+
const links = parser.parse(
|
|
673
|
+
'(form: stack (direction vertical) (save: button Save (on press (emit save))))'
|
|
674
|
+
);
|
|
675
|
+
const text = formatNested(links);
|
|
676
|
+
// form: stack
|
|
677
|
+
// direction vertical
|
|
678
|
+
// save: button Save
|
|
679
|
+
// on press
|
|
680
|
+
// emit save
|
|
681
|
+
|
|
682
|
+
const unindent = ({ element, children }) =>
|
|
683
|
+
children.length
|
|
684
|
+
? new Link(element.id, [...element.values, ...children.map(unindent)])
|
|
685
|
+
: element;
|
|
686
|
+
const readBack = parser.parseGroups(text).map(unindent);
|
|
687
|
+
console.log(readBack[0].equals(links[0])); // true
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
Run the complete [form example](../examples/js_nested_format.mjs) with Node.
|
|
691
|
+
|
|
692
|
+
Use `parseGroups` plus this fold to read nested output. Canonical `parse` keeps
|
|
693
|
+
its path expansion for ordinary indented heads. `formatIndentedDocument` remains
|
|
694
|
+
the separate two-level plain-text document helper.
|
|
695
|
+
|
|
696
|
+
The default indentation is two spaces; `formatNested(links, ' ')` uses four.
|
|
697
|
+
The result has no final newline. References use normal escaping. Parentheses
|
|
698
|
+
stay where needed for `()`, `((a b))`, or a lone reference under a bare `name:`
|
|
699
|
+
ancestor. An atom after a link stays in place, so `(root: (a b) tail (c d))`
|
|
700
|
+
becomes `root: (a b) tail\n c d` without changing value order. The existing
|
|
701
|
+
inline formatter's exclusions for empty links and named empty-value links used
|
|
702
|
+
as values also apply to this writer.
|
|
703
|
+
|
|
704
|
+
Threshold options (`maxInlineRefs`, `indentLongLines`, `preferInline`) retain
|
|
705
|
+
anonymous links inline when indentation would lose their container. Use
|
|
706
|
+
`formatNested` for recursive output. The same writer is available in all ten
|
|
707
|
+
bindings; see the [cross-language API table](../parity/README.md#recursive-nested-formatting).
|
package/README.ru.md
CHANGED
|
@@ -213,6 +213,16 @@ stream.end('следующая связь');
|
|
|
213
213
|
`listenerCount` и другие). До версии 0.27.1 `StreamParser` импортировал модуль
|
|
214
214
|
`events`, и для браузерной сборки требовался пакет-полифил `events`.
|
|
215
215
|
|
|
216
|
+
### Группы отступных ID
|
|
217
|
+
|
|
218
|
+
В JavaScript, C++, Swift и Kotlin доступен `parseGroups`.
|
|
219
|
+
Для отступного ID, например `x:\n a\n b`, элемент `x:` имеет пустой
|
|
220
|
+
список значений, а его дети — ссылки `a` и `b`. Свёртка детей в значения
|
|
221
|
+
элемента даёт `(x: a b)`, как канонический `parse`. Явное `(a)` сохраняет
|
|
222
|
+
связь с одним значением; обычный заголовок `x: kind` сохраняет прежние
|
|
223
|
+
дочерние связи и развёртку путей. Начиная с 0.30.2, ссылка в листе доступна
|
|
224
|
+
как `child.element`, а не `child.element.values[0]`.
|
|
225
|
+
|
|
216
226
|
## Примеры синтаксиса
|
|
217
227
|
|
|
218
228
|
### Дуплеты (2-кортежи)
|
package/dist/index.js
CHANGED
|
@@ -439,9 +439,7 @@ class Link {
|
|
|
439
439
|
}
|
|
440
440
|
_formatIndented(options) {
|
|
441
441
|
if (this.id === null) {
|
|
442
|
-
|
|
443
|
-
return lines.join(`
|
|
444
|
-
`);
|
|
442
|
+
return this.format(options.lessParentheses);
|
|
445
443
|
}
|
|
446
444
|
const idStr = Link.escapeReference(this.id);
|
|
447
445
|
const lines = [`${idStr}:`];
|
|
@@ -2943,8 +2941,10 @@ class Parser {
|
|
|
2943
2941
|
this.parse(input);
|
|
2944
2942
|
return buildCst(input, { comments: this.comments });
|
|
2945
2943
|
}
|
|
2946
|
-
_transformGroup(item) {
|
|
2947
|
-
|
|
2944
|
+
_transformGroup(item, indentedValue = false) {
|
|
2945
|
+
const children = item.children || [];
|
|
2946
|
+
const childValues = indentedValue || item.id != null && !item.values?.length;
|
|
2947
|
+
return new LinksGroup(indentedValue && children.length === 0 ? this.transformIndentedValue(item) : this.transformLink(item), children.map((child) => this._transformGroup(child, childValues)));
|
|
2948
2948
|
}
|
|
2949
2949
|
_parse(input, transform) {
|
|
2950
2950
|
if (typeof input !== "string") {
|
|
@@ -4069,6 +4069,34 @@ class BinaryLinoCodec {
|
|
|
4069
4069
|
return this.formatDocument(this.decode(bytes));
|
|
4070
4070
|
}
|
|
4071
4071
|
}
|
|
4072
|
+
// src/NestedFormat.js
|
|
4073
|
+
var isAtom = (link) => link.id !== null && link.values.length === 0;
|
|
4074
|
+
var canNest = (link) => !isAtom(link) && !(link.id === null && link.values.length === 1 && !isAtom(link.values[0]));
|
|
4075
|
+
var lineHolds = (link, count) => link.id !== null || count >= 2 || count === 1 && isAtom(link.values[0]);
|
|
4076
|
+
function nestedLines(link, pad, indent, underName = false) {
|
|
4077
|
+
if (!lineHolds(link, link.values.length) || underName && link.id === null && link.values.length === 1) {
|
|
4078
|
+
return [pad + link.format()];
|
|
4079
|
+
}
|
|
4080
|
+
let count = link.values.length;
|
|
4081
|
+
while (count > 0 && canNest(link.values[count - 1]))
|
|
4082
|
+
count--;
|
|
4083
|
+
while (count < link.values.length && !lineHolds(link, count))
|
|
4084
|
+
count++;
|
|
4085
|
+
const values = link.values.slice(0, count).map((value) => isAtom(value) ? Link.escapeReference(value.id) : value.format());
|
|
4086
|
+
if (link.id !== null)
|
|
4087
|
+
values.unshift(`${Link.escapeReference(link.id)}:`);
|
|
4088
|
+
return [
|
|
4089
|
+
pad + values.join(" "),
|
|
4090
|
+
...link.values.slice(count).flatMap((value) => nestedLines(value, pad + indent, indent, underName || link.id !== null && count === 0))
|
|
4091
|
+
];
|
|
4092
|
+
}
|
|
4093
|
+
function formatNested(links, indent = " ") {
|
|
4094
|
+
if (typeof indent !== "string" || !/^ +$/.test(indent)) {
|
|
4095
|
+
throw new TypeError("indent must contain only spaces and be nonempty");
|
|
4096
|
+
}
|
|
4097
|
+
return links.flatMap((link) => nestedLines(link, "", indent)).join(`
|
|
4098
|
+
`);
|
|
4099
|
+
}
|
|
4072
4100
|
export {
|
|
4073
4101
|
ArityRange,
|
|
4074
4102
|
BinaryLinoCodec,
|
|
@@ -4096,6 +4124,7 @@ export {
|
|
|
4096
4124
|
formatBinaryReference,
|
|
4097
4125
|
formatIndentedDocument,
|
|
4098
4126
|
formatLinks,
|
|
4127
|
+
formatNested,
|
|
4099
4128
|
parseIndentedDocument,
|
|
4100
4129
|
referenceQuote,
|
|
4101
4130
|
stripComments,
|
package/index.d.ts
CHANGED
|
@@ -264,7 +264,11 @@ export class Parser {
|
|
|
264
264
|
*/
|
|
265
265
|
parse(input: string): Link[];
|
|
266
266
|
|
|
267
|
-
/**
|
|
267
|
+
/**
|
|
268
|
+
* Read the indentation tree without expanding it into path combinations.
|
|
269
|
+
* Indented ID elements have no values; their single-reference leaf children
|
|
270
|
+
* are references, so folding an indented ID subtree agrees with parse().
|
|
271
|
+
*/
|
|
268
272
|
parseGroups(input: string): IndentedGroup[];
|
|
269
273
|
|
|
270
274
|
/**
|
|
@@ -720,3 +724,6 @@ export function formatBinaryReference(reference: string): string;
|
|
|
720
724
|
export function encodeReferenceLiteral(text: string): string;
|
|
721
725
|
/** Decode a complete literal. Throws on malformed hex, UTF-8, or unsupported versions. */
|
|
722
726
|
export function decodeReferenceLiteral(literal: string): string;
|
|
727
|
+
|
|
728
|
+
/** Write recursive nested lines, read by folding Parser.parseGroups(). No final newline. */
|
|
729
|
+
export function formatNested(links: Link[], indent?: string): string;
|
package/package.json
CHANGED
package/src/Link.js
CHANGED
|
@@ -383,11 +383,8 @@ export class Link {
|
|
|
383
383
|
*/
|
|
384
384
|
_formatIndented(options) {
|
|
385
385
|
if (this.id === null) {
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
(v) => options.indentString + this.formatValue(v)
|
|
389
|
-
);
|
|
390
|
-
return lines.join('\n');
|
|
386
|
+
// A headless list reads as separate links. Keep the inline container.
|
|
387
|
+
return this.format(options.lessParentheses);
|
|
391
388
|
}
|
|
392
389
|
|
|
393
390
|
// Link with ID - format as id:\n value1\n value2
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Link } from './Link.js';
|
|
2
|
+
|
|
3
|
+
const isAtom = (link) => link.id !== null && link.values.length === 0;
|
|
4
|
+
const canNest = (link) =>
|
|
5
|
+
!isAtom(link) &&
|
|
6
|
+
!(link.id === null && link.values.length === 1 && !isAtom(link.values[0]));
|
|
7
|
+
const lineHolds = (link, count) =>
|
|
8
|
+
link.id !== null || count >= 2 || (count === 1 && isAtom(link.values[0]));
|
|
9
|
+
|
|
10
|
+
function nestedLines(link, pad, indent, underName = false) {
|
|
11
|
+
if (
|
|
12
|
+
!lineHolds(link, link.values.length) ||
|
|
13
|
+
(underName && link.id === null && link.values.length === 1)
|
|
14
|
+
) {
|
|
15
|
+
return [pad + link.format()];
|
|
16
|
+
}
|
|
17
|
+
// Only move a trailing run: moving a link past a later atom changes order.
|
|
18
|
+
let count = link.values.length;
|
|
19
|
+
while (count > 0 && canNest(link.values[count - 1])) count--;
|
|
20
|
+
while (count < link.values.length && !lineHolds(link, count)) count++;
|
|
21
|
+
const values = link.values
|
|
22
|
+
.slice(0, count)
|
|
23
|
+
.map((value) =>
|
|
24
|
+
isAtom(value) ? Link.escapeReference(value.id) : value.format()
|
|
25
|
+
);
|
|
26
|
+
if (link.id !== null) values.unshift(`${Link.escapeReference(link.id)}:`);
|
|
27
|
+
return [
|
|
28
|
+
pad + values.join(' '),
|
|
29
|
+
...link.values
|
|
30
|
+
.slice(count)
|
|
31
|
+
.flatMap((value) =>
|
|
32
|
+
nestedLines(
|
|
33
|
+
value,
|
|
34
|
+
pad + indent,
|
|
35
|
+
indent,
|
|
36
|
+
underName || (link.id !== null && count === 0)
|
|
37
|
+
)
|
|
38
|
+
),
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Write recursively nested lines. Read with Parser.parseGroups(), appending
|
|
44
|
+
* each group's children to its element's values. Ordinary parse() retains
|
|
45
|
+
* its path-expansion semantics. No final newline is added.
|
|
46
|
+
* @param {Link[]} links - Links in document order
|
|
47
|
+
* @param {string} [indent=' '] - Nonempty spaces for one indentation level
|
|
48
|
+
* @returns {string} Nested Links Notation
|
|
49
|
+
*/
|
|
50
|
+
export function formatNested(links, indent = ' ') {
|
|
51
|
+
if (typeof indent !== 'string' || !/^ +$/.test(indent)) {
|
|
52
|
+
throw new TypeError('indent must contain only spaces and be nonempty');
|
|
53
|
+
}
|
|
54
|
+
return links.flatMap((link) => nestedLines(link, '', indent)).join('\n');
|
|
55
|
+
}
|
package/src/Parser.js
CHANGED
|
@@ -42,6 +42,8 @@ export class Parser {
|
|
|
42
42
|
* Read the indentation tree before it is expanded into path combinations.
|
|
43
43
|
* Each group's element is the Link on that line; its children are groups
|
|
44
44
|
* for the immediately indented lines. Parenthesized values remain Links.
|
|
45
|
+
* In an indented ID value subtree, single-reference leaves are references,
|
|
46
|
+
* as in parse(); the ID element itself keeps an empty value list.
|
|
45
47
|
* @param {string} input - The Lino document
|
|
46
48
|
* @returns {LinksGroup[]}
|
|
47
49
|
*/
|
|
@@ -65,10 +67,18 @@ export class Parser {
|
|
|
65
67
|
return buildCst(input, { comments: this.comments });
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
_transformGroup(item) {
|
|
70
|
+
_transformGroup(item, indentedValue = false) {
|
|
71
|
+
const children = item.children || [];
|
|
72
|
+
// Carry the value context through an indented ID subtree, just as
|
|
73
|
+
// transformIndentedValue does. Keep non-leaf containers: folding their
|
|
74
|
+
// children later must retain the reference written on the parent line.
|
|
75
|
+
const childValues =
|
|
76
|
+
indentedValue || (item.id != null && !item.values?.length);
|
|
69
77
|
return new LinksGroup(
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
indentedValue && children.length === 0
|
|
79
|
+
? this.transformIndentedValue(item)
|
|
80
|
+
: this.transformLink(item),
|
|
81
|
+
children.map((child) => this._transformGroup(child, childValues))
|
|
72
82
|
);
|
|
73
83
|
}
|
|
74
84
|
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { URL } from 'node:url';
|
|
3
|
+
import { expect, test } from 'bun:test';
|
|
4
|
+
import { Link, Parser } from '../src/index.js';
|
|
5
|
+
|
|
6
|
+
const unindent = ({ element, children }) =>
|
|
7
|
+
children.length
|
|
8
|
+
? new Link(element.id, [...element.values, ...children.map(unindent)])
|
|
9
|
+
: element;
|
|
10
|
+
|
|
11
|
+
const fixtures = JSON.parse(
|
|
12
|
+
readFileSync(
|
|
13
|
+
new URL('../../parity/text-fixtures.json', import.meta.url),
|
|
14
|
+
'utf8'
|
|
15
|
+
)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
test('indented ID groups fold to the same links as parse (#381)', () => {
|
|
19
|
+
for (const input of [
|
|
20
|
+
'x:\n a\n b',
|
|
21
|
+
"'x':\n 'a'\n ~1{62}",
|
|
22
|
+
'x:\n (a)\n b c\n child: value',
|
|
23
|
+
'x:\n inner:\n a\n branch\n b',
|
|
24
|
+
'x:\n child: kind\n a\n (branch)\n b',
|
|
25
|
+
'x:\r\n a # comment\r\n\r\n b',
|
|
26
|
+
...fixtures
|
|
27
|
+
.filter((fixture) => fixture.id.startsWith('indented-id-'))
|
|
28
|
+
.map((fixture) => fixture.input),
|
|
29
|
+
]) {
|
|
30
|
+
const parser = new Parser();
|
|
31
|
+
expect(parser.parseGroups(input).map(unindent), input).toEqual(
|
|
32
|
+
parser.parse(input)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('a bare ID group is a self reference with an empty value list', () => {
|
|
38
|
+
const parser = new Parser();
|
|
39
|
+
for (const input of ['id:', '(id:)', 'id:\n a']) {
|
|
40
|
+
const [group] = parser.parseGroups(input);
|
|
41
|
+
expect(group.element.id).toBe('id');
|
|
42
|
+
expect(group.element.values).toEqual([]);
|
|
43
|
+
expect(group.children).toHaveLength(input.includes('\n') ? 1 : 0);
|
|
44
|
+
}
|
|
45
|
+
expect(parser.parse('id:')).toEqual([new Link('id', [])]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('reference children keep syntax metadata and explicit parentheses', () => {
|
|
49
|
+
const [group] = new Parser().parseGroups("'x':\n 'a'\n (b)");
|
|
50
|
+
expect(group.element.rawId).toBe("'x'");
|
|
51
|
+
expect(group.children[0].element.id).toBe('a');
|
|
52
|
+
expect(group.children[0].element.rawId).toBe("'a'");
|
|
53
|
+
expect(group.children[0].element.quote).toBe("'");
|
|
54
|
+
expect(group.children[1].element).toEqual(new Link(null, [new Link('b')]));
|
|
55
|
+
const [ast] = new Parser({ syntax: false }).parseGroups("x:\n 'a'");
|
|
56
|
+
expect(ast.children[0].element.rawId).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('ordinary heads retain one-value child links and path expansion', () => {
|
|
60
|
+
const parser = new Parser();
|
|
61
|
+
for (const input of ['x\n a\n b', 'x: kind\n a\n b']) {
|
|
62
|
+
const [group] = parser.parseGroups(input);
|
|
63
|
+
expect(group.children.map((child) => child.element)).toEqual([
|
|
64
|
+
new Link(null, [new Link('a')]),
|
|
65
|
+
new Link(null, [new Link('b')]),
|
|
66
|
+
]);
|
|
67
|
+
expect(parser.parse(input)).toHaveLength(3);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { URL } from 'node:url';
|
|
3
|
+
import { expect, test } from 'bun:test';
|
|
4
|
+
import * as lino from '../src/index.js';
|
|
5
|
+
|
|
6
|
+
const { Parser, Link, FormatOptions, formatLinks } = lino;
|
|
7
|
+
const parser = new Parser();
|
|
8
|
+
const fold = ({ element, children }) =>
|
|
9
|
+
children.length
|
|
10
|
+
? new Link(element.id, [...element.values, ...children.map(fold)])
|
|
11
|
+
: element;
|
|
12
|
+
const tree = (links) =>
|
|
13
|
+
links.map(({ id, values }) => ({ id, values: tree(values) }));
|
|
14
|
+
const form =
|
|
15
|
+
"(form: stack (direction vertical) (gap 8px) (save: button 'Save' (on press (emit save) (navigate home))))";
|
|
16
|
+
|
|
17
|
+
test('recursive nested writer formats the issue form and reads back through groups', () => {
|
|
18
|
+
const links = parser.parse(form);
|
|
19
|
+
const text = lino.formatNested(links);
|
|
20
|
+
expect(text).toBe(
|
|
21
|
+
'form: stack\n direction vertical\n gap 8px\n save: button Save\n on press\n emit save\n navigate home'
|
|
22
|
+
);
|
|
23
|
+
expect(tree(parser.parseGroups(text).map(fold))).toEqual(tree(links));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('anonymous threshold output retains the anonymous container', () => {
|
|
27
|
+
for (const input of ['a b c', '((b c))', '(a (b c))']) {
|
|
28
|
+
const links = parser.parse(input);
|
|
29
|
+
for (const threshold of [
|
|
30
|
+
{ maxInlineRefs: 1 },
|
|
31
|
+
{ indentLongLines: true, maxLineLength: 1 },
|
|
32
|
+
]) {
|
|
33
|
+
const text = formatLinks(
|
|
34
|
+
links,
|
|
35
|
+
new FormatOptions({
|
|
36
|
+
lessParentheses: true,
|
|
37
|
+
preferInline: false,
|
|
38
|
+
...threshold,
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
expect(tree(parser.parse(text)), input).toEqual(tree(links));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('nested writer folds every existing shared text fixture without changing values', () => {
|
|
47
|
+
const fixtures = JSON.parse(
|
|
48
|
+
readFileSync(
|
|
49
|
+
new URL('../../parity/text-fixtures.json', import.meta.url),
|
|
50
|
+
'utf8'
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
for (const fixture of fixtures) {
|
|
54
|
+
const links = parser.parse(fixture.input);
|
|
55
|
+
const text = lino.formatNested(links);
|
|
56
|
+
expect(tree(parser.parseGroups(text).map(fold)), fixture.id).toEqual(
|
|
57
|
+
tree(links)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('shared nested golden corpus and custom indentation read back exactly', () => {
|
|
63
|
+
const cases = JSON.parse(
|
|
64
|
+
readFileSync(
|
|
65
|
+
new URL('../../parity/nested-fixtures.json', import.meta.url),
|
|
66
|
+
'utf8'
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
for (const fixture of cases) {
|
|
70
|
+
const links = parser.parse(fixture.input);
|
|
71
|
+
expect(lino.formatNested(links), fixture.id).toBe(fixture.nested);
|
|
72
|
+
for (const indent of [' ', ' ']) {
|
|
73
|
+
const text = lino.formatNested(links, indent);
|
|
74
|
+
expect(tree(parser.parseGroups(text).map(fold)), fixture.id).toEqual(
|
|
75
|
+
tree(links)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
expect(() => lino.formatNested([], '')).toThrow();
|
|
80
|
+
expect(() => lino.formatNested([], 'x')).toThrow();
|
|
81
|
+
expect(() => lino.formatNested([], '\t')).toThrow();
|
|
82
|
+
});
|
|
@@ -75,5 +75,5 @@ test('line length is measured on the parentheses a lone nested value keeps', ()
|
|
|
75
75
|
preferInline: false,
|
|
76
76
|
});
|
|
77
77
|
expect(link.format(options(7))).toBe('((b c))');
|
|
78
|
-
expect(link.format(options(6))).toBe('
|
|
78
|
+
expect(link.format(options(6))).toBe('((b c))');
|
|
79
79
|
});
|
|
@@ -69,7 +69,7 @@ describe('syntax metadata on parsed links (issue #364)', () => {
|
|
|
69
69
|
test('parseGroups and StreamParser keep the metadata', () => {
|
|
70
70
|
const groups = new Parser().parseGroups('\'root\':\n "child"');
|
|
71
71
|
expect(groups[0].element.rawId).toBe("'root'");
|
|
72
|
-
expect(groups[0].children[0].element.
|
|
72
|
+
expect(groups[0].children[0].element.rawId).toBe('"child"');
|
|
73
73
|
const stream = new StreamParser();
|
|
74
74
|
stream.write("(a '");
|
|
75
75
|
stream.write("b')");
|