@effected/markdown 0.23.0 → 0.24.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/MarkdownDiagnostic.js +25 -37
- package/MarkdownDocument.js +66 -6
- package/README.md +2 -2
- package/index.d.ts +85 -125
- package/index.js +2 -1
- package/internal/blockParser.js +8 -18
- package/internal/phrasing.js +6 -15
- package/internal/point.js +32 -0
- package/package.json +7 -4
- package/tsdoc-metadata.json +1 -1
- package/internal/lineIndex.js +0 -75
package/MarkdownDiagnostic.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MARKDOWN_PARSE_ERROR_CODES } from "./internal/carriers.js";
|
|
2
|
+
import { LineIndex } from "@effected/source";
|
|
2
3
|
import { Schema } from "effect";
|
|
3
4
|
|
|
4
5
|
// src/MarkdownDiagnostic.ts
|
|
@@ -16,14 +17,20 @@ const MarkdownParseErrorCode = Schema.Literals(MARKDOWN_PARSE_ERROR_CODES);
|
|
|
16
17
|
* zero-based `line`/`character`).
|
|
17
18
|
*
|
|
18
19
|
* @remarks
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
20
|
+
* Satisfies `@effected/source`'s `SourceDiagnostic<MarkdownParseErrorCode>`:
|
|
21
|
+
* the positional core (`code`/`message`/`offset`/`length`/`line`/`character`)
|
|
22
|
+
* the kit's format packages share, so a consumer printing or serving
|
|
23
|
+
* diagnostics (an LSP server, a CLI annotation) is written once against that
|
|
24
|
+
* shape. `line`/`character` are zero-based, `character` in UTF-16 code units,
|
|
25
|
+
* with `\n`, `\r\n` and a bare `\r` as line breaks: exactly
|
|
26
|
+
* `LineIndex.make(source).positionAt(offset)`, and the same index
|
|
27
|
+
* {@link MarkdownDocument.lineIndex} returns. An offset at the `\n` of a
|
|
28
|
+
* `\r\n` reads as the end of its line.
|
|
29
|
+
*
|
|
30
|
+
* The one-based `line`/`column` unist `Point`s carried on `MarkdownNode`
|
|
31
|
+
* positions read the same index, one-based: a deliberate, different
|
|
32
|
+
* convention for the AST rather than a mismatch to reconcile (see
|
|
33
|
+
* {@link MarkdownDocument.lineIndex} for the exact mapping).
|
|
27
34
|
*
|
|
28
35
|
* @public
|
|
29
36
|
*/
|
|
@@ -40,12 +47,20 @@ var MarkdownDiagnostic = class MarkdownDiagnostic extends Schema.Class("Markdown
|
|
|
40
47
|
* from `offset` against the source text. Advanced — the parse entry
|
|
41
48
|
* points call this for you.
|
|
42
49
|
*
|
|
43
|
-
* @
|
|
50
|
+
* @remarks
|
|
51
|
+
* Pass a line index already built over the source to materialize many
|
|
52
|
+
* records without rescanning the text; a string builds one. The
|
|
53
|
+
* parameter is the structural `SourceIndex`, so an index from any
|
|
54
|
+
* installed copy of `@effected/source` is accepted. An offset
|
|
55
|
+
* outside the source clamps into it, as `LineIndex.positionAt` does.
|
|
56
|
+
*
|
|
57
|
+
* @param source - The source text the record's `offset` indexes into, or
|
|
58
|
+
* a `LineIndex` over it.
|
|
44
59
|
* @param raw - The engine record: `code`, `message`, `offset` and `length`.
|
|
45
60
|
* @returns The diagnostic with `line` and `character` filled in.
|
|
46
61
|
*/
|
|
47
62
|
static fromRaw(source, raw) {
|
|
48
|
-
const { line, character } =
|
|
63
|
+
const { line, character } = (typeof source === "string" ? LineIndex.make(source) : source).positionAt(raw.offset);
|
|
49
64
|
return MarkdownDiagnostic.make({
|
|
50
65
|
code: raw.code,
|
|
51
66
|
message: raw.message,
|
|
@@ -56,32 +71,5 @@ var MarkdownDiagnostic = class MarkdownDiagnostic extends Schema.Class("Markdown
|
|
|
56
71
|
});
|
|
57
72
|
}
|
|
58
73
|
};
|
|
59
|
-
/**
|
|
60
|
-
* Compute the zero-based line/character position of `offset` within `text`.
|
|
61
|
-
* Recognizes `\n`, `\r` and `\r\n` as line breaks; a CRLF pair counts as a
|
|
62
|
-
* single newline (toml `TomlDiagnostic` precedent — kept independent of
|
|
63
|
-
* `internal/lineIndex.ts`, which answers a different, one-based question
|
|
64
|
-
* for AST positions).
|
|
65
|
-
*/
|
|
66
|
-
function lineChar(text, offset) {
|
|
67
|
-
let line = 0;
|
|
68
|
-
let lineStart = 0;
|
|
69
|
-
const limit = Math.min(offset, text.length);
|
|
70
|
-
for (let i = 0; i < limit; i++) {
|
|
71
|
-
const ch = text.charCodeAt(i);
|
|
72
|
-
if (ch === 10) {
|
|
73
|
-
line++;
|
|
74
|
-
lineStart = i + 1;
|
|
75
|
-
} else if (ch === 13) {
|
|
76
|
-
if (i + 1 < text.length && text.charCodeAt(i + 1) === 10) i++;
|
|
77
|
-
line++;
|
|
78
|
-
lineStart = i + 1;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
line,
|
|
83
|
-
character: offset - lineStart
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
74
|
|
|
87
75
|
export { MarkdownDiagnostic, MarkdownParseErrorCode };
|
package/MarkdownDocument.js
CHANGED
|
@@ -6,6 +6,7 @@ import { normalizeLabelText } from "./internal/references.js";
|
|
|
6
6
|
import { MarkdownDiagnostic } from "./MarkdownDiagnostic.js";
|
|
7
7
|
import { rootHeadingBounds } from "./internal/sectionBounds.js";
|
|
8
8
|
import { parsePassResult } from "./Markdown.js";
|
|
9
|
+
import { LineIndex } from "@effected/source";
|
|
9
10
|
import { Effect, Option, Result, Schema } from "effect";
|
|
10
11
|
|
|
11
12
|
// src/MarkdownDocument.ts
|
|
@@ -122,6 +123,12 @@ const phrasingText = (nodes) => {
|
|
|
122
123
|
return out;
|
|
123
124
|
};
|
|
124
125
|
/**
|
|
126
|
+
* Each document's line index, built at most once. A side table rather than a
|
|
127
|
+
* field: the index is derived from `source`, so it is no part of the
|
|
128
|
+
* document's schema, its equality or its encoded form.
|
|
129
|
+
*/
|
|
130
|
+
const lineIndexes = /* @__PURE__ */ new WeakMap();
|
|
131
|
+
/**
|
|
125
132
|
* A parsed markdown document: the original `source`, the mdast-shaped
|
|
126
133
|
* {@link Root} tree, the non-fatal {@link MarkdownDiagnostic}s the parse
|
|
127
134
|
* produced, and the link-reference `definitions` index.
|
|
@@ -213,6 +220,55 @@ var MarkdownDocument = class MarkdownDocument extends Schema.Class("MarkdownDocu
|
|
|
213
220
|
return scanFrontmatter(preprocessLines(this.source), this.source) !== null;
|
|
214
221
|
}
|
|
215
222
|
/**
|
|
223
|
+
* The line index over `source`: offsets to zero-based `{ line, character }`
|
|
224
|
+
* and back, with exactly the rules this package's own positions use.
|
|
225
|
+
*
|
|
226
|
+
* @remarks
|
|
227
|
+
* A `LineIndex` from `@effected/source` (re-exported from this package's
|
|
228
|
+
* entry point), typed as the structural `SourceIndex` so a consumer on
|
|
229
|
+
* another installed copy of `@effected/source` can pass it on, with its
|
|
230
|
+
* default `"lsp"` line breaks: `\n`, `\r\n` and a
|
|
231
|
+
* bare `\r`, `character` in UTF-16 code units, an offset at the `\n` of a
|
|
232
|
+
* `\r\n` reading as the end of its line. So for any offset into
|
|
233
|
+
* `source`:
|
|
234
|
+
*
|
|
235
|
+
* - `doc.lineIndex.positionAt(offset)` is the `line`/`character` a
|
|
236
|
+
* {@link MarkdownDiagnostic} at that offset carries;
|
|
237
|
+
* - a node's unist `Point` has `line` = `lineAt(offset) + 1` and
|
|
238
|
+
* `column` = `offset - lineStart(line) + 1`: `positionAt` plus one on
|
|
239
|
+
* both axes everywhere except at the `\n` of a `\r\n`, which a Point
|
|
240
|
+
* counts as one column past the `\r` (as `vfile-location` does) and
|
|
241
|
+
* `positionAt` reads as the end of its line;
|
|
242
|
+
* - `offsetAt`, `spanOf` and `rangeOf` convert an LSP `Position` or
|
|
243
|
+
* `Range` back to an offset or a `MarkdownRange`-shaped
|
|
244
|
+
* `{ offset, length }`.
|
|
245
|
+
*
|
|
246
|
+
* A parsed document returns the index the parse itself built; a document
|
|
247
|
+
* constructed with `make` builds one on first access. Either way it is
|
|
248
|
+
* built once per document and shared by every later access. To index a
|
|
249
|
+
* text with no document, `LineIndex.make(text)` gives the same answers.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* import { MarkdownDocument } from "@effected/markdown";
|
|
254
|
+
* import { Result } from "effect";
|
|
255
|
+
*
|
|
256
|
+
* const result = MarkdownDocument.parseResult("# Title\r\n\r\nBody\n");
|
|
257
|
+
* if (Result.isSuccess(result)) {
|
|
258
|
+
* const index = result.success.lineIndex;
|
|
259
|
+
* console.log(index.positionAt(12)); // => { line: 2, character: 1 }
|
|
260
|
+
* console.log(index.offsetAt({ line: 2, character: 0 })); // => 11
|
|
261
|
+
* }
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
get lineIndex() {
|
|
265
|
+
const cached = lineIndexes.get(this);
|
|
266
|
+
if (cached !== void 0) return cached;
|
|
267
|
+
const index = LineIndex.make(this.source);
|
|
268
|
+
lineIndexes.set(this, index);
|
|
269
|
+
return index;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
216
272
|
* Every heading in the document, in document order, wherever it sits —
|
|
217
273
|
* including headings nested inside blockquotes and list items.
|
|
218
274
|
*
|
|
@@ -379,12 +435,16 @@ var MarkdownDocument = class MarkdownDocument extends Schema.Class("MarkdownDocu
|
|
|
379
435
|
* `MarkdownParseError`.
|
|
380
436
|
*/
|
|
381
437
|
static parseResult(text, options) {
|
|
382
|
-
return Result.map(parsePassResult(text, options), (pass) =>
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
438
|
+
return Result.map(parsePassResult(text, options), (pass) => {
|
|
439
|
+
const document = MarkdownDocument.make({
|
|
440
|
+
source: text,
|
|
441
|
+
root: pass.root,
|
|
442
|
+
diagnostics: pass.carriers.map((carrier) => MarkdownDiagnostic.fromRaw(pass.lineIndex, carrier)),
|
|
443
|
+
definitions: pass.refmap
|
|
444
|
+
});
|
|
445
|
+
lineIndexes.set(document, pass.lineIndex);
|
|
446
|
+
return document;
|
|
447
|
+
});
|
|
388
448
|
}
|
|
389
449
|
/**
|
|
390
450
|
* Parse markdown into a {@link MarkdownDocument}. Defined in terms of
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
CommonMark 0.31.2 and GFM parsing, editing and transformation expressed as Effect schemas and pure functions. Parse markdown into mdast-shaped nodes carrying byte offsets, navigate headings, sections and links, compute surgical offset-splice edits, normalize markers, decode frontmatter into a validated domain schema, project to and from plain mdast for the remark ecosystem, and walk a document as a `Stream`.
|
|
9
9
|
|
|
10
10
|
> **Pre-`1.0.0`.** This package is part of the `@effected/*` kit, built on stable
|
|
11
11
|
> Effect v4 (`effect` `^4.0.0`) and still in `0.x` development. Stable Effect
|
|
@@ -41,7 +41,7 @@ npm install @effected/markdown effect
|
|
|
41
41
|
pnpm add @effected/markdown effect
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Requires Node.js >=24.11.0. `effect` v4 is a peer dependency; the package
|
|
44
|
+
Requires Node.js >=24.11.0. `effect` v4 is a peer dependency; the package's one runtime dependency is the dependency-free `@effected/source`, whose `LineIndex` it re-exports.
|
|
45
45
|
|
|
46
46
|
`@effected/yaml`, `@effected/toml` and `@effected/jsonc` are **optional** peers, required only by the frontmatter codec module you import. A consumer decoding yaml frontmatter installs `@effected/yaml` and nothing else; a consumer who never touches frontmatter installs none of them.
|
|
47
47
|
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LineBreaks, LineIndex, LineIndexOptions, SourceDiagnostic, SourceIndex, SourceIndex as SourceIndex$1, SourceLocation, SourcePosition, SourceRange, SourceSpan } from "@effected/source";
|
|
1
2
|
import { Data, Effect, Option, Result, Schema, Stream } from "effect";
|
|
2
3
|
declare const Point_base: Schema.Class<Point, Schema.Struct<{
|
|
3
4
|
readonly line: Schema.Number;
|
|
@@ -968,14 +969,20 @@ declare const MarkdownDiagnostic_base: Schema.Class<MarkdownDiagnostic, Schema.S
|
|
|
968
969
|
* zero-based `line`/`character`).
|
|
969
970
|
*
|
|
970
971
|
* @remarks
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
* the
|
|
974
|
-
*
|
|
975
|
-
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
972
|
+
* Satisfies `@effected/source`'s `SourceDiagnostic<MarkdownParseErrorCode>`:
|
|
973
|
+
* the positional core (`code`/`message`/`offset`/`length`/`line`/`character`)
|
|
974
|
+
* the kit's format packages share, so a consumer printing or serving
|
|
975
|
+
* diagnostics (an LSP server, a CLI annotation) is written once against that
|
|
976
|
+
* shape. `line`/`character` are zero-based, `character` in UTF-16 code units,
|
|
977
|
+
* with `\n`, `\r\n` and a bare `\r` as line breaks: exactly
|
|
978
|
+
* `LineIndex.make(source).positionAt(offset)`, and the same index
|
|
979
|
+
* {@link MarkdownDocument.lineIndex} returns. An offset at the `\n` of a
|
|
980
|
+
* `\r\n` reads as the end of its line.
|
|
981
|
+
*
|
|
982
|
+
* The one-based `line`/`column` unist `Point`s carried on `MarkdownNode`
|
|
983
|
+
* positions read the same index, one-based: a deliberate, different
|
|
984
|
+
* convention for the AST rather than a mismatch to reconcile (see
|
|
985
|
+
* {@link MarkdownDocument.lineIndex} for the exact mapping).
|
|
979
986
|
*
|
|
980
987
|
* @public
|
|
981
988
|
*/
|
|
@@ -985,11 +992,19 @@ export declare class MarkdownDiagnostic extends MarkdownDiagnostic_base {
|
|
|
985
992
|
* from `offset` against the source text. Advanced — the parse entry
|
|
986
993
|
* points call this for you.
|
|
987
994
|
*
|
|
988
|
-
* @
|
|
995
|
+
* @remarks
|
|
996
|
+
* Pass a line index already built over the source to materialize many
|
|
997
|
+
* records without rescanning the text; a string builds one. The
|
|
998
|
+
* parameter is the structural `SourceIndex`, so an index from any
|
|
999
|
+
* installed copy of `@effected/source` is accepted. An offset
|
|
1000
|
+
* outside the source clamps into it, as `LineIndex.positionAt` does.
|
|
1001
|
+
*
|
|
1002
|
+
* @param source - The source text the record's `offset` indexes into, or
|
|
1003
|
+
* a `LineIndex` over it.
|
|
989
1004
|
* @param raw - The engine record: `code`, `message`, `offset` and `length`.
|
|
990
1005
|
* @returns The diagnostic with `line` and `character` filled in.
|
|
991
1006
|
*/
|
|
992
|
-
static fromRaw(source: string, raw: {
|
|
1007
|
+
static fromRaw(source: string | SourceIndex$1, raw: {
|
|
993
1008
|
readonly code: MarkdownParseErrorCode;
|
|
994
1009
|
readonly message: string;
|
|
995
1010
|
readonly offset: number;
|
|
@@ -1653,6 +1668,49 @@ export declare class MarkdownDocument extends MarkdownDocument_base {
|
|
|
1653
1668
|
* checking repeatedly.
|
|
1654
1669
|
*/
|
|
1655
1670
|
get hasFrontmatterBlock(): boolean;
|
|
1671
|
+
/**
|
|
1672
|
+
* The line index over `source`: offsets to zero-based `{ line, character }`
|
|
1673
|
+
* and back, with exactly the rules this package's own positions use.
|
|
1674
|
+
*
|
|
1675
|
+
* @remarks
|
|
1676
|
+
* A `LineIndex` from `@effected/source` (re-exported from this package's
|
|
1677
|
+
* entry point), typed as the structural `SourceIndex` so a consumer on
|
|
1678
|
+
* another installed copy of `@effected/source` can pass it on, with its
|
|
1679
|
+
* default `"lsp"` line breaks: `\n`, `\r\n` and a
|
|
1680
|
+
* bare `\r`, `character` in UTF-16 code units, an offset at the `\n` of a
|
|
1681
|
+
* `\r\n` reading as the end of its line. So for any offset into
|
|
1682
|
+
* `source`:
|
|
1683
|
+
*
|
|
1684
|
+
* - `doc.lineIndex.positionAt(offset)` is the `line`/`character` a
|
|
1685
|
+
* {@link MarkdownDiagnostic} at that offset carries;
|
|
1686
|
+
* - a node's unist `Point` has `line` = `lineAt(offset) + 1` and
|
|
1687
|
+
* `column` = `offset - lineStart(line) + 1`: `positionAt` plus one on
|
|
1688
|
+
* both axes everywhere except at the `\n` of a `\r\n`, which a Point
|
|
1689
|
+
* counts as one column past the `\r` (as `vfile-location` does) and
|
|
1690
|
+
* `positionAt` reads as the end of its line;
|
|
1691
|
+
* - `offsetAt`, `spanOf` and `rangeOf` convert an LSP `Position` or
|
|
1692
|
+
* `Range` back to an offset or a `MarkdownRange`-shaped
|
|
1693
|
+
* `{ offset, length }`.
|
|
1694
|
+
*
|
|
1695
|
+
* A parsed document returns the index the parse itself built; a document
|
|
1696
|
+
* constructed with `make` builds one on first access. Either way it is
|
|
1697
|
+
* built once per document and shared by every later access. To index a
|
|
1698
|
+
* text with no document, `LineIndex.make(text)` gives the same answers.
|
|
1699
|
+
*
|
|
1700
|
+
* @example
|
|
1701
|
+
* ```ts
|
|
1702
|
+
* import { MarkdownDocument } from "@effected/markdown";
|
|
1703
|
+
* import { Result } from "effect";
|
|
1704
|
+
*
|
|
1705
|
+
* const result = MarkdownDocument.parseResult("# Title\r\n\r\nBody\n");
|
|
1706
|
+
* if (Result.isSuccess(result)) {
|
|
1707
|
+
* const index = result.success.lineIndex;
|
|
1708
|
+
* console.log(index.positionAt(12)); // => { line: 2, character: 1 }
|
|
1709
|
+
* console.log(index.offsetAt({ line: 2, character: 0 })); // => 11
|
|
1710
|
+
* }
|
|
1711
|
+
* ```
|
|
1712
|
+
*/
|
|
1713
|
+
get lineIndex(): SourceIndex$1;
|
|
1656
1714
|
/**
|
|
1657
1715
|
* Every heading in the document, in document order, wherever it sits —
|
|
1658
1716
|
* including headings nested inside blockquotes and list items.
|
|
@@ -3004,120 +3062,22 @@ export type MarkdownVisitorEvent = Data.TaggedEnum<{
|
|
|
3004
3062
|
*
|
|
3005
3063
|
* @public
|
|
3006
3064
|
*/
|
|
3007
|
-
export declare const MarkdownVisitorEvent: {
|
|
3008
|
-
readonly
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
readonly _tag: "Exit";
|
|
3024
|
-
readonly node: MarkdownNode;
|
|
3025
|
-
readonly path: MarkdownPath;
|
|
3026
|
-
readonly depth: number;
|
|
3027
|
-
}, {
|
|
3028
|
-
readonly _tag: Tag;
|
|
3029
|
-
}>;
|
|
3030
|
-
readonly $match: {
|
|
3031
|
-
<Cases extends {
|
|
3032
|
-
readonly Enter: (args: {
|
|
3033
|
-
readonly _tag: "Enter";
|
|
3034
|
-
readonly node: MarkdownNode;
|
|
3035
|
-
readonly path: MarkdownPath;
|
|
3036
|
-
readonly depth: number;
|
|
3037
|
-
}) => any;
|
|
3038
|
-
readonly Error: (args: {
|
|
3039
|
-
readonly _tag: "Error";
|
|
3040
|
-
readonly diagnostic: MarkdownDiagnostic;
|
|
3041
|
-
readonly path: MarkdownPath;
|
|
3042
|
-
readonly depth: number;
|
|
3043
|
-
}) => any;
|
|
3044
|
-
readonly Exit: (args: {
|
|
3045
|
-
readonly _tag: "Exit";
|
|
3046
|
-
readonly node: MarkdownNode;
|
|
3047
|
-
readonly path: MarkdownPath;
|
|
3048
|
-
readonly depth: number;
|
|
3049
|
-
}) => any;
|
|
3050
|
-
}>(cases: Cases): (value: {
|
|
3051
|
-
readonly _tag: "Enter";
|
|
3052
|
-
readonly node: MarkdownNode;
|
|
3053
|
-
readonly path: MarkdownPath;
|
|
3054
|
-
readonly depth: number;
|
|
3055
|
-
} | {
|
|
3056
|
-
readonly _tag: "Error";
|
|
3057
|
-
readonly diagnostic: MarkdownDiagnostic;
|
|
3058
|
-
readonly path: MarkdownPath;
|
|
3059
|
-
readonly depth: number;
|
|
3060
|
-
} | {
|
|
3061
|
-
readonly _tag: "Exit";
|
|
3062
|
-
readonly node: MarkdownNode;
|
|
3063
|
-
readonly path: MarkdownPath;
|
|
3064
|
-
readonly depth: number;
|
|
3065
|
-
}) => import("effect/Unify").Unify<ReturnType<Cases["Enter" | "Error" | "Exit"]>>;
|
|
3066
|
-
<Cases extends {
|
|
3067
|
-
readonly Enter: (args: {
|
|
3068
|
-
readonly _tag: "Enter";
|
|
3069
|
-
readonly node: MarkdownNode;
|
|
3070
|
-
readonly path: MarkdownPath;
|
|
3071
|
-
readonly depth: number;
|
|
3072
|
-
}) => any;
|
|
3073
|
-
readonly Error: (args: {
|
|
3074
|
-
readonly _tag: "Error";
|
|
3075
|
-
readonly diagnostic: MarkdownDiagnostic;
|
|
3076
|
-
readonly path: MarkdownPath;
|
|
3077
|
-
readonly depth: number;
|
|
3078
|
-
}) => any;
|
|
3079
|
-
readonly Exit: (args: {
|
|
3080
|
-
readonly _tag: "Exit";
|
|
3081
|
-
readonly node: MarkdownNode;
|
|
3082
|
-
readonly path: MarkdownPath;
|
|
3083
|
-
readonly depth: number;
|
|
3084
|
-
}) => any;
|
|
3085
|
-
}>(value: {
|
|
3086
|
-
readonly _tag: "Enter";
|
|
3087
|
-
readonly node: MarkdownNode;
|
|
3088
|
-
readonly path: MarkdownPath;
|
|
3089
|
-
readonly depth: number;
|
|
3090
|
-
} | {
|
|
3091
|
-
readonly _tag: "Error";
|
|
3092
|
-
readonly diagnostic: MarkdownDiagnostic;
|
|
3093
|
-
readonly path: MarkdownPath;
|
|
3094
|
-
readonly depth: number;
|
|
3095
|
-
} | {
|
|
3096
|
-
readonly _tag: "Exit";
|
|
3097
|
-
readonly node: MarkdownNode;
|
|
3098
|
-
readonly path: MarkdownPath;
|
|
3099
|
-
readonly depth: number;
|
|
3100
|
-
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Enter" | "Error" | "Exit"]>>;
|
|
3101
|
-
};
|
|
3102
|
-
readonly Enter: Data.TaggedEnum.ConstructorFrom<{
|
|
3103
|
-
readonly _tag: "Enter";
|
|
3104
|
-
readonly node: MarkdownNode;
|
|
3105
|
-
readonly path: MarkdownPath;
|
|
3106
|
-
readonly depth: number;
|
|
3107
|
-
}, "_tag">;
|
|
3108
|
-
readonly Error: Data.TaggedEnum.ConstructorFrom<{
|
|
3109
|
-
readonly _tag: "Error";
|
|
3110
|
-
readonly diagnostic: MarkdownDiagnostic;
|
|
3111
|
-
readonly path: MarkdownPath;
|
|
3112
|
-
readonly depth: number;
|
|
3113
|
-
}, "_tag">;
|
|
3114
|
-
readonly Exit: Data.TaggedEnum.ConstructorFrom<{
|
|
3115
|
-
readonly _tag: "Exit";
|
|
3116
|
-
readonly node: MarkdownNode;
|
|
3117
|
-
readonly path: MarkdownPath;
|
|
3118
|
-
readonly depth: number;
|
|
3119
|
-
}, "_tag">;
|
|
3120
|
-
};
|
|
3065
|
+
export declare const MarkdownVisitorEvent: Data.TaggedEnum.Constructor<{
|
|
3066
|
+
readonly _tag: "Enter";
|
|
3067
|
+
readonly node: MarkdownNode;
|
|
3068
|
+
readonly path: MarkdownPath;
|
|
3069
|
+
readonly depth: number;
|
|
3070
|
+
} | {
|
|
3071
|
+
readonly _tag: "Error";
|
|
3072
|
+
readonly diagnostic: MarkdownDiagnostic;
|
|
3073
|
+
readonly path: MarkdownPath;
|
|
3074
|
+
readonly depth: number;
|
|
3075
|
+
} | {
|
|
3076
|
+
readonly _tag: "Exit";
|
|
3077
|
+
readonly node: MarkdownNode;
|
|
3078
|
+
readonly path: MarkdownPath;
|
|
3079
|
+
readonly depth: number;
|
|
3080
|
+
}>;
|
|
3121
3081
|
/**
|
|
3122
3082
|
* Walks a parsed markdown tree as a lazy sequence of `Enter`/`Exit` events in
|
|
3123
3083
|
* document pre-order — synchronously with {@link MarkdownVisitor.walk}, or as
|
|
@@ -3443,5 +3403,5 @@ export declare const TomlFrontmatter: FrontmatterCodec;
|
|
|
3443
3403
|
* @public
|
|
3444
3404
|
*/
|
|
3445
3405
|
export declare const YamlFrontmatter: FrontmatterCodec;
|
|
3446
|
-
export type
|
|
3406
|
+
export { type DocumentHeading, type DocumentLink, type FrontmatterCodec, type FrontmatterResolveError, type FrontmatterSchemaError, type FrontmatterSchemaResolver, type FrontmatterWriteError, type LineBreaks, LineIndex, type LineIndexOptions, type LinkBearingNode, type MarkdownNodeOfType, type MarkdownNodeType, type MarkdownPath, type MarkdownPhrasingOnlyType, type MarkdownRangeLike, type MarkdownSegment, type MarkdownStringifyOptions, type MarkdownTransformHandlers, type MarkdownTransformResult, type MdastNode, type MdastToTextOptions, type SectionHeadingMatch, type SectionQueryOptions, type SectionSpan, type SectionSpanOptions, type ShiftHeadingsOptions, type SourceDiagnostic, type SourceIndex, type SourceLocation, type SourcePosition, type SourceRange, type SourceSpan };
|
|
3447
3407
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -13,5 +13,6 @@ import { MarkdownVisitor, MarkdownVisitorEvent } from "./MarkdownVisitor.js";
|
|
|
13
13
|
import { Mdast, MdastDecodeError } from "./Mdast.js";
|
|
14
14
|
import { TomlFrontmatter } from "./TomlFrontmatter.js";
|
|
15
15
|
import { YamlFrontmatter } from "./YamlFrontmatter.js";
|
|
16
|
+
import { LineIndex } from "@effected/source";
|
|
16
17
|
|
|
17
|
-
export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterNewline, FrontmatterSource, FrontmatterSourceBlock, FrontmatterSourceSplit, FrontmatterValidationError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, MarkdownRange, MarkdownStringifyError, MarkdownTransform, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeContent, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
|
|
18
|
+
export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterNewline, FrontmatterSource, FrontmatterSourceBlock, FrontmatterSourceSplit, FrontmatterValidationError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, LineIndex, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, MarkdownRange, MarkdownStringifyError, MarkdownTransform, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeContent, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
|
package/internal/blockParser.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { Frontmatter,
|
|
1
|
+
import { Frontmatter, Position, Root } from "../MarkdownNode.js";
|
|
2
2
|
import { scanFrontmatter } from "./blocks/frontmatter.js";
|
|
3
3
|
import { makeBlockNode } from "./blockTypes.js";
|
|
4
4
|
import { columnsToNextTabStop, preprocessLines } from "./preprocess.js";
|
|
5
5
|
import { isHtmlBlockEnd } from "./blocks/htmlBlock.js";
|
|
6
6
|
import { blockDialect } from "./blockRegistry.js";
|
|
7
7
|
import { GuardExceeded } from "./carriers.js";
|
|
8
|
-
import {
|
|
8
|
+
import { pointAt } from "./point.js";
|
|
9
9
|
import { prepareInline } from "./rawInline.js";
|
|
10
|
+
import { LineIndex } from "@effected/source";
|
|
10
11
|
|
|
11
12
|
// src/internal/blockParser.ts
|
|
12
13
|
/** Upstream's cheap pre-filter: a line that cannot start any block. */
|
|
@@ -42,9 +43,7 @@ var BlockParser = class {
|
|
|
42
43
|
constructor(text, dialect, dialectName, frontmatter = false) {
|
|
43
44
|
this.lines = preprocessLines(text);
|
|
44
45
|
this.frontmatterCapture = frontmatter ? scanFrontmatter(this.lines, text) : null;
|
|
45
|
-
|
|
46
|
-
if (text.endsWith("\n")) starts.push(text.length);
|
|
47
|
-
this.lineIndex = LineIndex.fromLineStarts(text, starts);
|
|
46
|
+
this.lineIndex = LineIndex.make(text);
|
|
48
47
|
this.sourceLength = text.length;
|
|
49
48
|
this.dialect = dialect;
|
|
50
49
|
this.dialectName = dialectName;
|
|
@@ -306,19 +305,9 @@ var BlockParser = class {
|
|
|
306
305
|
position(startOffset, endOffset) {
|
|
307
306
|
const start = Math.min(Math.max(startOffset, 0), this.sourceLength);
|
|
308
307
|
const end = Math.min(Math.max(endOffset, start), this.sourceLength);
|
|
309
|
-
const startPoint = this.lineIndex.positionAt(start);
|
|
310
|
-
const endPoint = this.lineIndex.positionAt(end);
|
|
311
308
|
return Position.make({
|
|
312
|
-
start:
|
|
313
|
-
|
|
314
|
-
column: startPoint.column,
|
|
315
|
-
offset: start
|
|
316
|
-
}),
|
|
317
|
-
end: Point.make({
|
|
318
|
-
line: endPoint.line,
|
|
319
|
-
column: endPoint.column,
|
|
320
|
-
offset: end
|
|
321
|
-
})
|
|
309
|
+
start: pointAt(this.lineIndex, start),
|
|
310
|
+
end: pointAt(this.lineIndex, end)
|
|
322
311
|
});
|
|
323
312
|
}
|
|
324
313
|
/**
|
|
@@ -402,7 +391,8 @@ var BlockParser = class {
|
|
|
402
391
|
rawInlines,
|
|
403
392
|
carriers: [],
|
|
404
393
|
refmap,
|
|
405
|
-
footnoteLabels
|
|
394
|
+
footnoteLabels,
|
|
395
|
+
lineIndex: this.lineIndex
|
|
406
396
|
};
|
|
407
397
|
}
|
|
408
398
|
};
|
package/internal/phrasing.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Position } from "../MarkdownNode.js";
|
|
2
2
|
import { preprocessLines } from "./preprocess.js";
|
|
3
|
-
import {
|
|
3
|
+
import { pointAt } from "./point.js";
|
|
4
4
|
import { parseInlines } from "./inlineParser.js";
|
|
5
5
|
import { trimWithSegments } from "./rawInline.js";
|
|
6
|
+
import { LineIndex } from "@effected/source";
|
|
6
7
|
|
|
7
8
|
// src/internal/phrasing.ts
|
|
8
9
|
const EMPTY_REFMAP = /* @__PURE__ */ new Map();
|
|
@@ -26,24 +27,14 @@ const parsePhrasingText = (text, dialect) => {
|
|
|
26
27
|
if (trimmed.text.length === 0) return [];
|
|
27
28
|
const first = trimmed.segments[0];
|
|
28
29
|
const startOffset = first === void 0 ? 0 : first.sourceOffset;
|
|
29
|
-
const lineIndex = LineIndex.
|
|
30
|
+
const lineIndex = LineIndex.make(text);
|
|
30
31
|
const sourceLength = text.length;
|
|
31
32
|
const positionOf = (start, end) => {
|
|
32
33
|
const clampedStart = Math.min(Math.max(start, 0), sourceLength);
|
|
33
34
|
const clampedEnd = Math.min(Math.max(end, clampedStart), sourceLength);
|
|
34
|
-
const startPoint = lineIndex.positionAt(clampedStart);
|
|
35
|
-
const endPoint = lineIndex.positionAt(clampedEnd);
|
|
36
35
|
return Position.make({
|
|
37
|
-
start:
|
|
38
|
-
|
|
39
|
-
column: startPoint.column,
|
|
40
|
-
offset: clampedStart
|
|
41
|
-
}),
|
|
42
|
-
end: Point.make({
|
|
43
|
-
line: endPoint.line,
|
|
44
|
-
column: endPoint.column,
|
|
45
|
-
offset: clampedEnd
|
|
46
|
-
})
|
|
36
|
+
start: pointAt(lineIndex, clampedStart),
|
|
37
|
+
end: pointAt(lineIndex, clampedEnd)
|
|
47
38
|
});
|
|
48
39
|
};
|
|
49
40
|
return parseInlines({
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Point } from "../MarkdownNode.js";
|
|
2
|
+
|
|
3
|
+
// src/internal/point.ts
|
|
4
|
+
/**
|
|
5
|
+
* The unist `Point` of `offset` within `index`'s text: one-based `line` and
|
|
6
|
+
* `column`, plus the offset itself.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* The caller clamps `offset` into `[0, text.length]` first, so `offset` is
|
|
10
|
+
* always the point's own offset.
|
|
11
|
+
*
|
|
12
|
+
* The column is `offset - lineStart(line) + 1`, NOT `positionAt(offset)`
|
|
13
|
+
* plus one. The two differ at exactly one offset class, the `\n` of a
|
|
14
|
+
* `\r\n`: `positionAt` reads it as the end of its line (LSP cannot name the
|
|
15
|
+
* middle of a terminator), while a unist Point counts it as one column past
|
|
16
|
+
* the `\r`, as `vfile-location` and `mdast-util-from-markdown` do. Node
|
|
17
|
+
* positions DO land there: an empty list item before a CRLF ends at its `\n`
|
|
18
|
+
* (`"*\r\na"` ends the item at offset 2, line 1, column 3), so the
|
|
19
|
+
* `positionAt` form would change published positions.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
const pointAt = (index, offset) => {
|
|
24
|
+
const line = index.lineAt(offset);
|
|
25
|
+
return Point.make({
|
|
26
|
+
line: line + 1,
|
|
27
|
+
column: offset - index.lineStart(line) + 1,
|
|
28
|
+
offset
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { pointAt };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "CommonMark and GFM markdown parse, edit and transform schemas for Effect",
|
|
6
6
|
"keywords": [
|
|
@@ -38,10 +38,13 @@
|
|
|
38
38
|
},
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@effected/source": "^0.1.0"
|
|
43
|
+
},
|
|
41
44
|
"peerDependencies": {
|
|
42
|
-
"@effected/jsonc": "^0.
|
|
43
|
-
"@effected/toml": "^0.
|
|
44
|
-
"@effected/yaml": "^0.
|
|
45
|
+
"@effected/jsonc": "^0.21.0",
|
|
46
|
+
"@effected/toml": "^0.17.0",
|
|
47
|
+
"@effected/yaml": "^0.26.0",
|
|
45
48
|
"effect": "^4.0.0"
|
|
46
49
|
},
|
|
47
50
|
"peerDependenciesMeta": {
|
package/tsdoc-metadata.json
CHANGED
package/internal/lineIndex.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// src/internal/lineIndex.ts
|
|
2
|
-
/**
|
|
3
|
-
* A precomputed index of line-start offsets over a fixed source text,
|
|
4
|
-
* answering `positionAt(offset)` in `O(log n)` via binary search rather than
|
|
5
|
-
* rescanning the text per query.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* Only `\n` begins a new line. A `\r` immediately before a `\n` (CRLF) is
|
|
9
|
-
* left as the trailing character of the prior line — the pair still reads as
|
|
10
|
-
* a single boundary because the following `\n` is what advances the line
|
|
11
|
-
* counter, not the `\r`. A bare `\r` (old Mac line endings) is not
|
|
12
|
-
* recognized as a break; CommonMark's preprocessing pass normalizes those
|
|
13
|
-
* before this index is ever built.
|
|
14
|
-
*/
|
|
15
|
-
var LineIndex = class LineIndex {
|
|
16
|
-
text;
|
|
17
|
-
lineStarts;
|
|
18
|
-
constructor(text, lineStarts) {
|
|
19
|
-
this.text = text;
|
|
20
|
-
this.lineStarts = lineStarts;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Build a {@link LineIndex} over `text` with one forward scan.
|
|
24
|
-
*
|
|
25
|
-
* @remarks
|
|
26
|
-
* Recognizes only `\n`, which is why the block pass hands over its own
|
|
27
|
-
* line table instead — see {@link LineIndex.fromLineStarts}.
|
|
28
|
-
*/
|
|
29
|
-
static make(text) {
|
|
30
|
-
const lineStarts = [0];
|
|
31
|
-
for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) lineStarts.push(i + 1);
|
|
32
|
-
return new LineIndex(text, lineStarts);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Build a {@link LineIndex} over `text` from a line table someone else
|
|
36
|
-
* already computed.
|
|
37
|
-
*
|
|
38
|
-
* @remarks
|
|
39
|
-
* The parser splits lines on `\r\n`, `\n` AND a bare `\r`; this index's
|
|
40
|
-
* own scan recognizes only `\n`. For any document without a bare `\r` the
|
|
41
|
-
* two agree, but for one with them they disagree about what a line is, and
|
|
42
|
-
* every reported line number would be wrong. Handing the parser's own
|
|
43
|
-
* table over removes the possibility rather than documenting it: there is
|
|
44
|
-
* one definition of a line, and it belongs to the preprocessor.
|
|
45
|
-
*
|
|
46
|
-
* `starts` must be ascending and begin at 0; a table that is neither is a
|
|
47
|
-
* wiring bug and dies as a defect.
|
|
48
|
-
*/
|
|
49
|
-
static fromLineStarts(text, starts) {
|
|
50
|
-
if (starts.length === 0 || starts[0] !== 0) throw new TypeError("line index: a line table must be non-empty and start at offset 0");
|
|
51
|
-
return new LineIndex(text, starts);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* The 1-based `{ line, column }` of `offset` within this index's text.
|
|
55
|
-
* Out-of-range offsets clamp to the nearest valid position (`0` or
|
|
56
|
-
* `text.length`) rather than throwing.
|
|
57
|
-
*/
|
|
58
|
-
positionAt(offset) {
|
|
59
|
-
const clamped = Math.min(Math.max(offset, 0), this.text.length);
|
|
60
|
-
let low = 0;
|
|
61
|
-
let high = this.lineStarts.length - 1;
|
|
62
|
-
while (low < high) {
|
|
63
|
-
const mid = low + high + 1 >> 1;
|
|
64
|
-
if ((this.lineStarts[mid] ?? 0) <= clamped) low = mid;
|
|
65
|
-
else high = mid - 1;
|
|
66
|
-
}
|
|
67
|
-
const lineStart = this.lineStarts[low] ?? 0;
|
|
68
|
-
return {
|
|
69
|
-
line: low + 1,
|
|
70
|
-
column: clamped - lineStart + 1
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export { LineIndex };
|