@stxt-lang/core 0.14.0 → 0.15.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 +5 -1
- package/out/core/LineParser.js +1 -1
- package/out/core/Parser.js +7 -0
- package/out/core/TextNode.d.ts +7 -0
- package/out/core/TextNode.js +11 -0
- package/out/runtime/Formatter.d.ts +10 -5
- package/out/runtime/Formatter.js +21 -10
- package/out/runtime/NodeWriter.js +10 -2
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Parser and schema validator for **STXT**, an indentation-based structured-text f
|
|
|
5
5
|
STXT is a plain-text format for writing structured, semantic documents: no braces, no closing tags, just indentation. It is designed to be equally readable by humans and by machines, and it comes with an optional schema layer so documents can be validated.
|
|
6
6
|
|
|
7
7
|
- Website and language reference: <https://stxt.dev>
|
|
8
|
-
- VSCode extension: [STXT
|
|
8
|
+
- VSCode extension: [STXT Language](https://marketplace.visualstudio.com/items?itemName=stxt-lang.stxt)
|
|
9
9
|
- Java implementation: [`dev.stxt:stxt-core`](https://central.sonatype.com/artifact/dev.stxt/stxt-core) on Maven Central
|
|
10
10
|
- Python implementation: [`stxt`](https://pypi.org/project/stxt/) on PyPI
|
|
11
11
|
|
|
@@ -334,6 +334,10 @@ if (errors.length === 0) {
|
|
|
334
334
|
}
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
+
`Formatter.format` takes the same limits as the parser as an optional third argument —
|
|
338
|
+
`Formatter.format(source, IndentStyle.TABS, { maxInputSize: -1 })` — since formatting parses
|
|
339
|
+
the document with them (STXT-SPEC §11.2).
|
|
340
|
+
|
|
337
341
|
## API surface
|
|
338
342
|
|
|
339
343
|
Everything importable from the package:
|
package/out/core/LineParser.js
CHANGED
|
@@ -64,7 +64,7 @@ function parseLine(line, lastNodeBlock, lastLevel, numLine, validate = true) {
|
|
|
64
64
|
if (lastNodeBlock && level > lastLevel) {
|
|
65
65
|
const text = StringUtils_1.StringUtils.rightTrim(line.substring(pointer + 1));
|
|
66
66
|
// The prefix covering the block level must be homogeneous (spec 10.2, rule 2);
|
|
67
|
-
// empty lines are
|
|
67
|
+
// empty lines are never an error and are exempt from it (spec 10.3)
|
|
68
68
|
if (validate && sawSpace && sawTab && text.length > 0) {
|
|
69
69
|
throw new ParseException_1.ParseException(numLine, "INDENTATION_MIXED", `Mixed tabs and spaces in indentation`);
|
|
70
70
|
}
|
package/out/core/Parser.js
CHANGED
|
@@ -257,6 +257,13 @@ class Parser {
|
|
|
257
257
|
closeToLevel(stack, targetLevel, result) {
|
|
258
258
|
while (stack.length > targetLevel) {
|
|
259
259
|
const completed = stack.pop();
|
|
260
|
+
// A closing block node drops its final empty lines (STXT-SPEC §10.3): they are not
|
|
261
|
+
// content, only visual separation or an editor's final line breaks. The validators
|
|
262
|
+
// and observers below already see the trimmed node; onTextLine did fire for these
|
|
263
|
+
// lines while the block was open, as process observation of the source.
|
|
264
|
+
if (completed instanceof TextNode_1.TextNode) {
|
|
265
|
+
completed.removeTrailingEmptyLines();
|
|
266
|
+
}
|
|
260
267
|
// Hand it over to the validators
|
|
261
268
|
this.validators.forEach(validator => {
|
|
262
269
|
try {
|
package/out/core/TextNode.d.ts
CHANGED
|
@@ -56,6 +56,13 @@ export declare class TextNode extends Node {
|
|
|
56
56
|
addTextLine(line: string): void;
|
|
57
57
|
/** Removes every text line. */
|
|
58
58
|
clearText(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Removes the final empty lines (`""` elements at the end of the lines). The {@link Parser}
|
|
61
|
+
* calls it when the block closes (STXT-SPEC §10.3: the final empty lines of a block are not
|
|
62
|
+
* content); it is public because a programmatically built node may want the same
|
|
63
|
+
* normalization before writing.
|
|
64
|
+
*/
|
|
65
|
+
removeTrailingEmptyLines(): void;
|
|
59
66
|
getText(): string;
|
|
60
67
|
isTextNode(): boolean;
|
|
61
68
|
private static splitLines;
|
package/out/core/TextNode.js
CHANGED
|
@@ -61,6 +61,17 @@ class TextNode extends Node_1.Node {
|
|
|
61
61
|
clearText() {
|
|
62
62
|
this.lines.length = 0;
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Removes the final empty lines (`""` elements at the end of the lines). The {@link Parser}
|
|
66
|
+
* calls it when the block closes (STXT-SPEC §10.3: the final empty lines of a block are not
|
|
67
|
+
* content); it is public because a programmatically built node may want the same
|
|
68
|
+
* normalization before writing.
|
|
69
|
+
*/
|
|
70
|
+
removeTrailingEmptyLines() {
|
|
71
|
+
while (this.lines.length > 0 && this.lines[this.lines.length - 1] === "") {
|
|
72
|
+
this.lines.pop();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
64
75
|
getText() {
|
|
65
76
|
return this.lines.join("\n");
|
|
66
77
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ParserOptions } from "../core/Parser";
|
|
1
2
|
import { ParseException } from "../exceptions/ParseException";
|
|
2
3
|
import { IndentStyle } from "./NodeWriter";
|
|
3
4
|
/** The outcome of {@link Formatter.format}. */
|
|
@@ -32,10 +33,10 @@ export interface FormatResult {
|
|
|
32
33
|
* - A **text line of a block** gets the indentation of the block (its level plus one) in the
|
|
33
34
|
* requested style, followed by its content; any indentation the line had beyond the block's
|
|
34
35
|
* is content (STXT-SPEC §10.2, relative indentation is preserved) and is kept exactly. A
|
|
35
|
-
* blank line
|
|
36
|
-
* (STXT-SPEC §10.3), so it is written with the indentation of the block too: the
|
|
37
|
-
* as one piece
|
|
38
|
-
*
|
|
36
|
+
* blank line that precedes more block text is `""` in the content whatever it looks like in
|
|
37
|
+
* the source (STXT-SPEC §10.3), so it is written with the indentation of the block too: the
|
|
38
|
+
* block reads as one piece. The final blank lines of a block are not content (STXT-SPEC
|
|
39
|
+
* §10.3: the parser drops them when the block closes) and fall under the next rule.
|
|
39
40
|
* - Every **other line** — a comment, a blank line outside a block, or a line the parse tree
|
|
40
41
|
* does not describe because of a syntax error — is kept as the author wrote it, except that
|
|
41
42
|
* its trailing blanks are removed and the whole indentation units at its start are converted
|
|
@@ -58,9 +59,13 @@ export declare class Formatter {
|
|
|
58
59
|
*
|
|
59
60
|
* @param text the document.
|
|
60
61
|
* @param style indentation style to format with; tabs by default.
|
|
62
|
+
* @param options limits for the internal parser (STXT-SPEC 11.2); every omitted one keeps
|
|
63
|
+
* its recommended default, and -1 disables one. A limit exceeded shows up in
|
|
64
|
+
* the errors like any other syntax error, and the lines the aborted parse
|
|
65
|
+
* never described are converted as "other lines" (indentation units only).
|
|
61
66
|
* @returns the formatted text and the syntax errors found; see {@link FormatResult}.
|
|
62
67
|
*/
|
|
63
|
-
static format(text: string, style?: IndentStyle): FormatResult;
|
|
68
|
+
static format(text: string, style?: IndentStyle, options?: ParserOptions): FormatResult;
|
|
64
69
|
/**
|
|
65
70
|
* Formats one source line.
|
|
66
71
|
*
|
package/out/runtime/Formatter.js
CHANGED
|
@@ -23,10 +23,10 @@ const NodeWriter_1 = require("./NodeWriter");
|
|
|
23
23
|
* - A **text line of a block** gets the indentation of the block (its level plus one) in the
|
|
24
24
|
* requested style, followed by its content; any indentation the line had beyond the block's
|
|
25
25
|
* is content (STXT-SPEC §10.2, relative indentation is preserved) and is kept exactly. A
|
|
26
|
-
* blank line
|
|
27
|
-
* (STXT-SPEC §10.3), so it is written with the indentation of the block too: the
|
|
28
|
-
* as one piece
|
|
29
|
-
*
|
|
26
|
+
* blank line that precedes more block text is `""` in the content whatever it looks like in
|
|
27
|
+
* the source (STXT-SPEC §10.3), so it is written with the indentation of the block too: the
|
|
28
|
+
* block reads as one piece. The final blank lines of a block are not content (STXT-SPEC
|
|
29
|
+
* §10.3: the parser drops them when the block closes) and fall under the next rule.
|
|
30
30
|
* - Every **other line** — a comment, a blank line outside a block, or a line the parse tree
|
|
31
31
|
* does not describe because of a syntax error — is kept as the author wrote it, except that
|
|
32
32
|
* its trailing blanks are removed and the whole indentation units at its start are converted
|
|
@@ -49,15 +49,19 @@ class Formatter {
|
|
|
49
49
|
*
|
|
50
50
|
* @param text the document.
|
|
51
51
|
* @param style indentation style to format with; tabs by default.
|
|
52
|
+
* @param options limits for the internal parser (STXT-SPEC 11.2); every omitted one keeps
|
|
53
|
+
* its recommended default, and -1 disables one. A limit exceeded shows up in
|
|
54
|
+
* the errors like any other syntax error, and the lines the aborted parse
|
|
55
|
+
* never described are converted as "other lines" (indentation units only).
|
|
52
56
|
* @returns the formatted text and the syntax errors found; see {@link FormatResult}.
|
|
53
57
|
*/
|
|
54
|
-
static format(text, style = NodeWriter_1.IndentStyle.TABS) {
|
|
58
|
+
static format(text, style = NodeWriter_1.IndentStyle.TABS, options) {
|
|
55
59
|
// STXT-TREE-SPEC 12.1: an initial BOM is not kept
|
|
56
60
|
if (text.startsWith("\uFEFF")) {
|
|
57
61
|
text = text.substring(1);
|
|
58
62
|
}
|
|
59
63
|
const sourceLines = new SourceLines();
|
|
60
|
-
const parser = new Parser_1.Parser();
|
|
64
|
+
const parser = new Parser_1.Parser(options);
|
|
61
65
|
parser.registerObserver(sourceLines);
|
|
62
66
|
const result = parser.parseResult(text);
|
|
63
67
|
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
@@ -81,8 +85,11 @@ class Formatter {
|
|
|
81
85
|
if (node) {
|
|
82
86
|
return Formatter.renderNode(node, line, style);
|
|
83
87
|
}
|
|
88
|
+
// A final empty line of a block is not content (STXT-SPEC §10.3): the parser removed it
|
|
89
|
+
// from the node when the block closed, so its index falls beyond the logical lines. It
|
|
90
|
+
// is kept as any other line: blank, unindented.
|
|
84
91
|
const text = sourceLines.textAt(lineNumber);
|
|
85
|
-
if (text) {
|
|
92
|
+
if (text && text.index < text.node.getTextLines().length) {
|
|
86
93
|
return Formatter.indent(text.node.getLevel() + 1, style) + text.line.content;
|
|
87
94
|
}
|
|
88
95
|
return Formatter.convertUnits(StringUtils_1.StringUtils.rightTrim(line), style);
|
|
@@ -168,7 +175,10 @@ class SourceLines {
|
|
|
168
175
|
// Comment lines need no bookkeeping: every line that opens no node is treated alike.
|
|
169
176
|
}
|
|
170
177
|
onTextLine(node, lineNumber, lineString, line) {
|
|
171
|
-
|
|
178
|
+
// The line was just appended: its 0-based index in the block is the current last. After
|
|
179
|
+
// the block closes and drops its final empty lines (STXT-SPEC §10.3), an index beyond
|
|
180
|
+
// getTextLines() marks the line as a final empty line, not content.
|
|
181
|
+
this.textByLine.set(lineNumber, { node, line, index: node.getTextLines().length - 1 });
|
|
172
182
|
}
|
|
173
183
|
/**
|
|
174
184
|
* @param lineNumber line number, 1-indexed.
|
|
@@ -179,8 +189,9 @@ class SourceLines {
|
|
|
179
189
|
}
|
|
180
190
|
/**
|
|
181
191
|
* @param lineNumber line number, 1-indexed.
|
|
182
|
-
* @returns the block node this line is text of
|
|
183
|
-
*
|
|
192
|
+
* @returns the block node this line is text of, the line already split into indentation and
|
|
193
|
+
* content, and its 0-based index in the block; or undefined if the line is not text
|
|
194
|
+
* of a block.
|
|
184
195
|
*/
|
|
185
196
|
textAt(lineNumber) {
|
|
186
197
|
return this.textByLine.get(lineNumber);
|
|
@@ -59,9 +59,17 @@ class NodeWriter {
|
|
|
59
59
|
}
|
|
60
60
|
if (n instanceof TextNode_1.TextNode) {
|
|
61
61
|
out.push(" >>\n");
|
|
62
|
-
|
|
62
|
+
// Final empty lines are not emitted (STXT-TREE-SPEC 11.1 rule 6): parsing never
|
|
63
|
+
// produces them (STXT-SPEC 10.3), and on a programmatically built node they would
|
|
64
|
+
// not survive the round trip.
|
|
65
|
+
const lines = n.getTextLines();
|
|
66
|
+
let last = lines.length;
|
|
67
|
+
while (last > 0 && lines[last - 1] === "") {
|
|
68
|
+
last--;
|
|
69
|
+
}
|
|
70
|
+
for (let i = 0; i < last; i++) {
|
|
63
71
|
NodeWriter.indent(out, depth + 1, style);
|
|
64
|
-
out.push(
|
|
72
|
+
out.push(lines[i], "\n");
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
else if (n instanceof InlineNode_1.InlineNode) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stxt-lang/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Parser and schema validator for STXT, an indentation-based structured-text format.",
|
|
5
5
|
"main": "out/all.js",
|
|
6
6
|
"types": "out/all.d.ts",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
},
|
|
35
35
|
"keywords": [
|
|
36
36
|
"stxt",
|
|
37
|
-
"semantic text",
|
|
38
37
|
"parser",
|
|
39
38
|
"schema",
|
|
40
39
|
"validation",
|