mdrefine 0.2.0-rc.2 → 0.2.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/CHANGELOG.md +577 -0
- package/CODE_OF_CONDUCT.md +30 -0
- package/CONTRIBUTING.md +189 -16
- package/README.md +100 -154
- package/SECURITY.md +15 -13
- package/config.schema.json +91 -0
- package/dist/cli/diff.d.ts +10 -0
- package/dist/cli/diff.js +247 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/main.js +270 -150
- package/dist/cli/main.js.map +1 -1
- package/dist/config/load.js +67 -6
- package/dist/config/load.js.map +1 -1
- package/dist/config/problems.d.ts +7 -0
- package/dist/config/problems.js +208 -0
- package/dist/config/problems.js.map +1 -0
- package/dist/config/resolve.d.ts +42 -109
- package/dist/config/resolve.js +193 -52
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/directives.d.ts +7 -0
- package/dist/core/directives.js +25 -13
- package/dist/core/directives.js.map +1 -1
- package/dist/core/engine.d.ts +42 -4
- package/dist/core/engine.js +518 -79
- package/dist/core/engine.js.map +1 -1
- package/dist/core/frozen.d.ts +6 -0
- package/dist/core/frozen.js +15 -0
- package/dist/core/frozen.js.map +1 -0
- package/dist/core/scope.d.ts +30 -0
- package/dist/core/scope.js +122 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/types.d.ts +202 -23
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +3 -0
- package/dist/node.js +4 -0
- package/dist/node.js.map +1 -0
- package/dist/rules/blocks.d.ts +34 -0
- package/dist/rules/blocks.js +550 -0
- package/dist/rules/blocks.js.map +1 -0
- package/dist/rules/content.d.ts +2 -0
- package/dist/rules/content.js +85 -0
- package/dist/rules/content.js.map +1 -0
- package/dist/rules/dialects.js +10 -6
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/links.js +302 -6
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/lists.d.ts +2 -0
- package/dist/rules/lists.js +924 -0
- package/dist/rules/lists.js.map +1 -0
- package/dist/rules/structure.js +127 -1
- package/dist/rules/structure.js.map +1 -1
- package/dist/rules/style.d.ts +8 -2
- package/dist/rules/style.js +1137 -165
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/block-identifier.d.ts +18 -0
- package/dist/syntax/block-identifier.js +24 -0
- package/dist/syntax/block-identifier.js.map +1 -0
- package/dist/syntax/obsidian.d.ts +29 -11
- package/dist/syntax/obsidian.js +9 -10
- package/dist/syntax/obsidian.js.map +1 -1
- package/dist/syntax/parse.d.ts +87 -3
- package/dist/syntax/parse.js +247 -19
- package/dist/syntax/parse.js.map +1 -1
- package/dist/workspace/access.d.ts +12 -3
- package/dist/workspace/access.js +33 -5
- package/dist/workspace/access.js.map +1 -1
- package/dist/workspace/discovery.d.ts +85 -0
- package/dist/workspace/discovery.js +155 -0
- package/dist/workspace/discovery.js.map +1 -0
- package/dist/workspace/encoding.d.ts +24 -0
- package/dist/workspace/encoding.js +113 -0
- package/dist/workspace/encoding.js.map +1 -0
- package/dist/workspace/files.d.ts +92 -6
- package/dist/workspace/files.js +715 -59
- package/dist/workspace/files.js.map +1 -1
- package/dist/workspace/git.d.ts +54 -0
- package/dist/workspace/git.js +198 -0
- package/dist/workspace/git.js.map +1 -0
- package/dist/workspace/index.d.ts +58 -3
- package/dist/workspace/index.js +136 -37
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/selection.d.ts +2 -2
- package/dist/workspace/selection.js +3 -3
- package/dist/workspace/selection.js.map +1 -1
- package/dist/workspace/slug.js +8 -1
- package/dist/workspace/slug.js.map +1 -1
- package/dist/workspace/vault.js +3 -1
- package/dist/workspace/vault.js.map +1 -1
- package/docs/api.md +239 -0
- package/docs/benchmarks.md +140 -200
- package/docs/changelog.md +1 -0
- package/docs/cli.md +281 -89
- package/docs/configuration.md +193 -480
- package/docs/design.md +163 -284
- package/docs/dialects.md +169 -0
- package/docs/examples.md +151 -109
- package/docs/index.md +21 -37
- package/docs/integrations.md +144 -0
- package/docs/obsidian.md +91 -115
- package/docs/plugins.md +147 -212
- package/docs/quick-start.md +68 -74
- package/docs/rules.md +1237 -0
- package/docs/versions.md +56 -0
- package/examples/README.md +17 -14
- package/examples/automation/summarize-report.mjs +5 -2
- package/examples/library/lint-folder.mjs +5 -4
- package/examples/library/lint-workspace.mjs +32 -0
- package/examples/plugin/notes/release-checklist.md +2 -2
- package/examples/plugin/rules.mjs +17 -23
- package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +2 -2
- package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +2 -2
- package/examples/workspaces/repository-docs/after/README.md +2 -2
- package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +0 -2
- package/examples/workspaces/repository-docs/before/README.md +2 -2
- package/examples/workspaces/repository-docs/before/docs/guide.md +7 -7
- package/examples/workspaces/repository-docs/before/docs/reference.md +3 -1
- package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +0 -2
- package/package.json +38 -12
- package/docs/contributing.md +0 -218
- package/docs/releases/0.1.0-beta.1.md +0 -39
- package/docs/releases/0.1.0-beta.2.md +0 -83
- package/docs/releases/0.1.0-beta.3.md +0 -61
- package/docs/releases/0.1.0-beta.4.md +0 -50
- package/docs/releases/0.1.0-rc.1.md +0 -99
- package/docs/releases/0.2.0-rc.1.md +0 -141
- package/docs/releases/0.2.0-rc.2.md +0 -225
- package/docs/releases.md +0 -144
- package/docs/testing.md +0 -93
package/docs/design.md
CHANGED
|
@@ -1,284 +1,163 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
would
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
the
|
|
72
|
-
|
|
73
|
-
front
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
that
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
Heading
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
document. Forgejo and Gitea fragments read a heading's trailing attribute block
|
|
165
|
-
(`## Title {#id .class}`) by the rules of goldmark 1.8, which Forgejo 16 and
|
|
166
|
-
Gitea 1.26 and later use: an `id` is the heading's only anchor, matched as
|
|
167
|
-
written and never numbered, and any other valid block is left out of the text
|
|
168
|
-
that generates the anchor. That text comes from a second parse of the target
|
|
169
|
-
with the blocks blanked out, because goldmark removes a block before it parses
|
|
170
|
-
the heading's inline content. The block stays heading text for other dialects,
|
|
171
|
-
rules, and plugins. An `id` written with the `user-content-` prefix that the
|
|
172
|
-
renderers add themselves is matched without it. Not modeled are the older
|
|
173
|
-
attribute rules of Gitea before 1.26, which for example also read a block
|
|
174
|
-
followed by a closing `#` sequence, and attribute values nested more than 64
|
|
175
|
-
levels deep, which leave the block as heading text. A heading whose `id` is not
|
|
176
|
-
a string (`{id=5}`) has no anchor on Gitea 1.26 and later and makes Forgejo, and
|
|
177
|
-
Gitea before 1.26, render nothing for the document; `forgejo/heading-id` reports
|
|
178
|
-
it on both dialects, and the anchors are those of Gitea 1.26 and later. Block
|
|
179
|
-
identifiers are indexed from trailing text markers. Heading nesting paths,
|
|
180
|
-
property aliases, PDF subpaths, and every Obsidian plugin's syntax are not fully
|
|
181
|
-
supported. Embeds are preserved as embeds; image dimensions in their aliases
|
|
182
|
-
remain intact.
|
|
183
|
-
|
|
184
|
-
GitHub, Forgejo, and Gitea store every anchor with the prefix `user-content-`
|
|
185
|
-
and add it to a link's fragment unless the fragment already starts with it. A
|
|
186
|
-
fragment written with the prefix therefore reaches the same anchor as one
|
|
187
|
-
without, and is matched without it; Obsidian fragments stay literal. The three
|
|
188
|
-
also keep the `id` attribute of every element and the `name` of an `<a>` in a
|
|
189
|
-
document's HTML, prefixed the same way, so those are fragments too, read from
|
|
190
|
-
the inline and block HTML with comments dropped; GitHub lowercases them and
|
|
191
|
-
matches any fragment lowercased as well, including a heading's. They do not
|
|
192
|
-
number later headings. The scan follows HTML tokenization: comments, unclosed
|
|
193
|
-
ones to the end of their block, declarations, and the content of `<script>`,
|
|
194
|
-
`<textarea>`, and the other raw-text elements are skipped, a `<!--` inside a
|
|
195
|
-
quoted attribute value is part of the value, and every value of a repeated
|
|
196
|
-
attribute counts, since the renderers disagree on which one they keep. Entity
|
|
197
|
-
references in such attributes are not decoded. GitHub and Gitea 1.26 and later
|
|
198
|
-
also generate anchors for headings written as HTML, from their text content with
|
|
199
|
-
inner tags removed and character references decoded; GitHub numbers them in
|
|
200
|
-
document order together with the Markdown headings and also when the tag has an
|
|
201
|
-
`id`, Gitea numbers nothing and skips a tag with an `id`, and Forgejo generates
|
|
202
|
-
none. Headings inside HTML blocks and inline in a paragraph both count, a
|
|
203
|
-
heading's start tag ends a heading that is still open, and one left open at the
|
|
204
|
-
end of its HTML is not reported, although the renderers continue it through the
|
|
205
|
-
Markdown that follows. Numeric character references are decoded with or without
|
|
206
|
-
a semicolon; named ones only with it. GitHub's treatment was taken from a live
|
|
207
|
-
README and from the heading filter its rendering derives from, not from a page
|
|
208
|
-
with repeated or `id`-bearing HTML headings.
|
|
209
|
-
|
|
210
|
-
The workspace indexes eligible paths first and parses target Markdown only when
|
|
211
|
-
a fragment is checked. For the rest of that invocation it keeps the target's
|
|
212
|
-
anchors and nothing else: the parsed document, which takes many times the memory
|
|
213
|
-
of its source, is released as soon as the anchors are known. Versions up to
|
|
214
|
-
`0.2.0-rc.1` kept every parsed target until the run ended, so memory grew with
|
|
215
|
-
the number of linked documents. Anchors form three groups, one for Obsidian, one
|
|
216
|
-
for GitHub and CommonMark, and one for Forgejo and Gitea. A group is computed
|
|
217
|
-
the first time a link is checked against one of its dialects, so a GitHub or
|
|
218
|
-
Obsidian workspace never reads attribute blocks, which need a second parse, or
|
|
219
|
-
the Forgejo and Gitea anchors, and the forges never number anchors GitHub's way.
|
|
220
|
-
The command line checks each target against one group. A library host that
|
|
221
|
-
checks a target against a second group has it read and parsed once more, which
|
|
222
|
-
is cheaper overall than keeping every parsed document or computing every group
|
|
223
|
-
for every target. CLI discovery also defers reading Markdown until it is
|
|
224
|
-
selected or needed for a fragment. Built-in rule-schema validators are reused
|
|
225
|
-
across documents. A suffix lookup index is built on first use instead of
|
|
226
|
-
scanning every path for each shortened link. There is no persistent cache,
|
|
227
|
-
worker pool, watch service, editor extension, or language server yet. The
|
|
228
|
-
library has a pluggable workspace interface for specialized hosts.
|
|
229
|
-
|
|
230
|
-
Discovery skips a directory that the system refuses to let it read (`EACCES` or
|
|
231
|
-
`EPERM`) and carries on; every other read error is treated as a fault and stops
|
|
232
|
-
the run. A directory is read as a whole or not at all: one that can be listed
|
|
233
|
-
but not entered, or entered but not listed, is skipped like one that allows
|
|
234
|
-
neither, although the first reveals file names and the second would open a file
|
|
235
|
-
whose name is known. The index then holds the readable files only. Links whose
|
|
236
|
-
path leads into a skipped directory resolve as unreadable rather than missing.
|
|
237
|
-
Obsidian's search by note name cannot be made exact: a name with no readable
|
|
238
|
-
match is reported as unchecked, a name with one readable match is accepted but
|
|
239
|
-
not rewritten, like a note found in a place that Obsidian tries only after a
|
|
240
|
-
skipped directory, and a name with several is ambiguous either way. A file is
|
|
241
|
-
read only when it is processed or when a link's fragment needs its content, so a
|
|
242
|
-
refusal is met late: a selected file becomes an `engine/unreadable-file`
|
|
243
|
-
diagnostic that blocks the batch's write phase, and a link target makes the
|
|
244
|
-
fragment unchecked for the documents that link to it.
|
|
245
|
-
[Paths that cannot be read](configuration.md#paths-that-cannot-be-read)
|
|
246
|
-
describes the behavior.
|
|
247
|
-
|
|
248
|
-
`npm run benchmark` measures parsing, linting, and formatting of synthetic
|
|
249
|
-
repository documentation and a synthetic vault, through the library and through
|
|
250
|
-
the CLI, and prints an output hash for comparing implementations.
|
|
251
|
-
[Benchmarks](benchmarks.md) explains each measurement and records reference
|
|
252
|
-
results. They do not predict a particular workspace's throughput. Measure the
|
|
253
|
-
original workload before adding caches or workers.
|
|
254
|
-
|
|
255
|
-
Formatting retains the current parsed document within one call, reusing it
|
|
256
|
-
between phases and for final diagnostics. Every changed candidate is parsed and
|
|
257
|
-
checked against the original semantic fingerprint before becoming the next
|
|
258
|
-
phase's input; the original fingerprint is computed only once an edit produces a
|
|
259
|
-
candidate. The final pass, in which no phase changes the document, reuses its
|
|
260
|
-
style findings and runs only the remaining rules for the final diagnostics.
|
|
261
|
-
Suppression directives are scanned only when the source contains their prefix,
|
|
262
|
-
and rules traverse the tree with a plain recursive walk rather than a
|
|
263
|
-
closure-per-node visitor. No-op edits still undergo range and overlap
|
|
264
|
-
validation. This retains the plugin contract that rules must not mutate
|
|
265
|
-
documents.
|
|
266
|
-
|
|
267
|
-
The benchmark's library measurements compare formatting documents that need
|
|
268
|
-
edits with formatting their already-formatted output. They follow a warm-up run,
|
|
269
|
-
check outputs and diagnostics, and exclude discovery, I/O, CLI serialization,
|
|
270
|
-
and custom plugins; the command-line measurements include them. Run the
|
|
271
|
-
benchmark without competing workloads.
|
|
272
|
-
|
|
273
|
-
## Maintenance priorities
|
|
274
|
-
|
|
275
|
-
Prioritize reproducible document corruption and renderer disagreement over new
|
|
276
|
-
style options. Add a minimal regression case before a fix; verify that disabling
|
|
277
|
-
the affected rule preserves the source. Broaden dialect fixtures and resolver
|
|
278
|
-
compatibility before promising complete application parity. Benchmark real
|
|
279
|
-
synthetic vault sizes before adding caching or worker processes.
|
|
280
|
-
|
|
281
|
-
Future changes can add richer link policies, reference-definition formatting,
|
|
282
|
-
more container layout, versioned plugin contracts, and optional standalone
|
|
283
|
-
distribution. Each should extend the shared configuration and edit contracts
|
|
284
|
-
instead of creating a second formatter pipeline.
|
|
1
|
+
# How it works and its limits
|
|
2
|
+
|
|
3
|
+
## Safety model
|
|
4
|
+
|
|
5
|
+
Formatting does not print a document again from its syntax tree. Each style rule
|
|
6
|
+
proposes edits at positions in the original source, and everything outside those
|
|
7
|
+
edits stays byte for byte as it was. A rule that is turned off therefore leaves
|
|
8
|
+
its constructs as they were written, and syntax that the tool does not
|
|
9
|
+
understand survives unchanged.
|
|
10
|
+
|
|
11
|
+
The edits are applied in three phases, inline, block, and document, so that
|
|
12
|
+
edits that commonly overlap do not meet in one pass. After each phase that
|
|
13
|
+
changes the document, the tool parses the result again and compares its meaning
|
|
14
|
+
with the original: the syntax tree with the whitespace of prose normalized and
|
|
15
|
+
each link compared by the file it resolves to. When the meaning differs, the
|
|
16
|
+
phase tries the edits one rule at a time and leaves out the edits of each rule
|
|
17
|
+
that would change it, all of them, because one edit can depend on another; the
|
|
18
|
+
other rules' edits are kept. Each rule left out gets an `engine/unsafe-format`
|
|
19
|
+
error at the line where the meaning would first differ, as in
|
|
20
|
+
`style/wrap would change the parsed meaning at line 12; none of its edits were applied.`
|
|
21
|
+
Of two edits that overlap, the one that starts first is applied, and the other
|
|
22
|
+
rule runs again in the next pass. The phases repeat until nothing changes, for
|
|
23
|
+
at most eight passes. When rules undo each other or formatting does not settle,
|
|
24
|
+
`format` returns the document as it was with an `engine/unsafe-format` error
|
|
25
|
+
that names them. Either way the run ends with exit code `2`, and
|
|
26
|
+
`format --write` still writes the other documents, and this one when the edits
|
|
27
|
+
that were kept changed it.
|
|
28
|
+
|
|
29
|
+
The comparison is only as good as the parser's reading:
|
|
30
|
+
|
|
31
|
+
- It compares syntax trees, and does not run GitHub's, Forgejo's, Gitea's, or
|
|
32
|
+
Obsidian's renderer.
|
|
33
|
+
- It reads the document in its dialect, so it cannot notice that the dialect is
|
|
34
|
+
wrong. In CommonMark a table is an ordinary paragraph, and joining its lines
|
|
35
|
+
changes nothing that CommonMark sees. This is why the tool [assumes a
|
|
36
|
+
dialect](dialects.md#the-assumed-dialect) with more syntax, not less, when
|
|
37
|
+
none is named.
|
|
38
|
+
- The parser, micromark, follows CommonMark closely but not everywhere. Directly
|
|
39
|
+
below a paragraph, a list item that begins with another list marker, as in
|
|
40
|
+
`- 2. Step` or `- -`, is read as a list item holding text, where CommonMark
|
|
41
|
+
and GitHub read a nested list ([issue
|
|
42
|
+
#191](https://github.com/viell-dev/slop-markdown-tools/issues/191)).
|
|
43
|
+
Formatting cannot change such a document's meaning, because the comparison
|
|
44
|
+
uses the same reading before and after, but rules see text where GitHub shows
|
|
45
|
+
a list, and the rules that would change such a place leave it as written.
|
|
46
|
+
Below an empty list item (`-`), the parser takes the `[x]` that begins the
|
|
47
|
+
next line for a checkbox that belongs to no item, where GitHub shows the text
|
|
48
|
+
`[x]`, so a blank line between them, as `style/block-spacing` would add, is
|
|
49
|
+
refused as a change of meaning. The parser also keeps the spaces and tabs that
|
|
50
|
+
start a code span's next line, where CommonMark and GitHub leave them out, so
|
|
51
|
+
that a span `npm run` continued by an indented `build` reads
|
|
52
|
+
`npm run build`, and it takes a space or line ending off each end of a span
|
|
53
|
+
before its line endings become spaces, where CommonMark takes spaces off
|
|
54
|
+
after. The comparison reads a code span as GitHub shows it, also where
|
|
55
|
+
renderers differ: GitHub keeps the indentation of a lazy continuation line in
|
|
56
|
+
the span, and commonmark.js does not. `style/inline-code` and `style/wrap`
|
|
57
|
+
join a span to that text ([issue
|
|
58
|
+
#265](https://github.com/viell-dev/slop-markdown-tools/issues/265), [issue
|
|
59
|
+
#285](https://github.com/viell-dev/slop-markdown-tools/issues/285), [issue
|
|
60
|
+
#290](https://github.com/viell-dev/slop-markdown-tools/issues/290)). Directly
|
|
61
|
+
below a table that ends in a list item or block quote, the parser reads the
|
|
62
|
+
lines of another table as text, where GitHub shows a table, so `style/wrap`
|
|
63
|
+
and `style/block-spacing` leave text whose second line is a table delimiter
|
|
64
|
+
row as written ([issue
|
|
65
|
+
#283](https://github.com/viell-dev/slop-markdown-tools/issues/283)).
|
|
66
|
+
- Front matter needs a closing line. A first line of `---` or `+++` that no
|
|
67
|
+
later line closes is read as renderers read it, as a thematic break or text,
|
|
68
|
+
with the lists and block quotes below it; the parser's front matter extension
|
|
69
|
+
reads them as paragraphs there, so the tool leaves it out for such a document
|
|
70
|
+
([issue #278](https://github.com/viell-dev/slop-markdown-tools/issues/278)).
|
|
71
|
+
- Front matter ends at the next `---` or `+++` line, also one inside a code
|
|
72
|
+
block, for the parser, for site generators, and for GitHub's file view when
|
|
73
|
+
the front matter is a YAML mapping. GitHub's comments, and renderers without
|
|
74
|
+
front matter, read the code block. When a line of the front matter opens a
|
|
75
|
+
code block, an HTML block, or display math, and the closing line is content of
|
|
76
|
+
that block in the other reading, the tool reads the document without front
|
|
77
|
+
matter, and an edit has to keep the meaning of both readings. In such a
|
|
78
|
+
document, a rule whose edits would change either, such as joining the lines of
|
|
79
|
+
the front matter, is refused ([issue
|
|
80
|
+
#295](https://github.com/viell-dev/slop-markdown-tools/issues/295)).
|
|
81
|
+
|
|
82
|
+
Which `$…$` is math was observed on the renderers themselves: about 125 cases
|
|
83
|
+
rendered by GitHub, Forgejo, and Gitea, which the tests compare with the tool on
|
|
84
|
+
every run, and 22 cases that a person looked at in both of Obsidian's views. The
|
|
85
|
+
rule is one-sided on purpose: text taken for math is only left alone, while a
|
|
86
|
+
formula taken for prose could be wrapped or restyled, so a condition is applied
|
|
87
|
+
to a dialect only when its renderer was seen to apply it.
|
|
88
|
+
|
|
89
|
+
## Limits
|
|
90
|
+
|
|
91
|
+
Formatting:
|
|
92
|
+
|
|
93
|
+
- A code span, an image, a link's destination, or a word longer than the line
|
|
94
|
+
stays whole and exceeds the width. The width is a target, not a license to
|
|
95
|
+
split syntax.
|
|
96
|
+
- Paragraphs that wrapping cannot reconstruct safely are left as written;
|
|
97
|
+
[`style/wrap`](rules.md#stylewrap) lists them, and its `reportUnreflowed`
|
|
98
|
+
option reports them.
|
|
99
|
+
- Emphasis inside a word keeps `*` when the configured marker is `_`, because
|
|
100
|
+
`_` cannot mark emphasis there.
|
|
101
|
+
- Tables are aligned only outside lists and block quotes.
|
|
102
|
+
- Footnotes, strikethrough, and literal autolinks are read but have no rules of
|
|
103
|
+
their own. Reference definitions are checked but not rewritten.
|
|
104
|
+
- There is no formatting of code inside code blocks, no change of front matter
|
|
105
|
+
or other metadata, no generation of documents, and no renaming of files.
|
|
106
|
+
|
|
107
|
+
Files:
|
|
108
|
+
|
|
109
|
+
- Only UTF-8 text is read. Another encoding is not guessed, because a guess can
|
|
110
|
+
be wrong and the renderers read Markdown as UTF-8, and it is not decoded with
|
|
111
|
+
replacement characters, which the next edit would write into the file. See
|
|
112
|
+
[files that cannot be read](cli.md#files-that-cannot-be-read-or-written).
|
|
113
|
+
- `format --write` writes into the existing file, which keeps its permissions
|
|
114
|
+
and attributes but is not atomic: a crash or power failure during the write
|
|
115
|
+
can leave the file incomplete; see [what writing
|
|
116
|
+
keeps](cli.md#what-writing-keeps).
|
|
117
|
+
|
|
118
|
+
Syntax:
|
|
119
|
+
|
|
120
|
+
- Obsidian inline comments that span several paragraphs are not fully read;
|
|
121
|
+
suppress formatting around them or use comment blocks. Heading nesting paths
|
|
122
|
+
in links, property aliases, PDF subpaths, and the syntax of community plugins
|
|
123
|
+
are not supported.
|
|
124
|
+
- Forgejo's and Gitea's own syntax, such as definition lists, backslash math,
|
|
125
|
+
and shortlinks, is protected rather than read, and shortlinks are not checked
|
|
126
|
+
as links.
|
|
127
|
+
- Legacy `> **Note**` callouts, table-of-contents front matter, emoji
|
|
128
|
+
shortcodes, color previews, and issue or commit references are kept as text.
|
|
129
|
+
- GitHub reads an escaped `\$` as the start of a formula; the tool does not.
|
|
130
|
+
- GitHub also links bare addresses such as `[www.example.com]` in a second pass
|
|
131
|
+
that leaves no source positions. The tool does not read those links, so
|
|
132
|
+
plugins do not see them; no built-in rule acts on them.
|
|
133
|
+
|
|
134
|
+
Links:
|
|
135
|
+
|
|
136
|
+
- Links to the web are never fetched.
|
|
137
|
+
- Obsidian's resolution by note name is not a complete copy of Obsidian's: a
|
|
138
|
+
missing or ambiguous target is reported, never guessed. A directory is never
|
|
139
|
+
rewritten to a README or index note.
|
|
140
|
+
- When a directory cannot be read, a search by note name cannot be exact. A link
|
|
141
|
+
that finds no readable note is reported as not checkable, and one that finds a
|
|
142
|
+
single note is accepted but never rewritten.
|
|
143
|
+
- With the workspace root inside a vault, links to notes outside the root cannot
|
|
144
|
+
be checked; see [Obsidian
|
|
145
|
+
vaults](obsidian.md#a-workspace-root-inside-a-vault).
|
|
146
|
+
- Heading anchors are modeled as each renderer generates them, with these
|
|
147
|
+
exceptions: character references inside tags that GitHub shows as text; on
|
|
148
|
+
Gitea, the number of a footnote reference in a heading, and Markdown inside
|
|
149
|
+
`<textarea>`, `<plaintext>`, and other elements whose content HTML reads as
|
|
150
|
+
text; the attribute rules of Gitea before 1.26; attribute values nested more
|
|
151
|
+
than 64 levels deep; and character references in HTML `id` and `name`
|
|
152
|
+
attributes, which are not decoded.
|
|
153
|
+
|
|
154
|
+
Scale:
|
|
155
|
+
|
|
156
|
+
- The tool uses one processor core and keeps nothing between runs: there is no
|
|
157
|
+
cache, worker pool, watch mode, editor extension, or language server. Start-up
|
|
158
|
+
takes about a quarter of a second.
|
|
159
|
+
- While a document is parsed, it needs about 70 to 130 times its size in memory,
|
|
160
|
+
and a single paragraph of 100 kB or more full of inline syntax parses slowly;
|
|
161
|
+
one made of tens of thousands of square brackets can take seconds from about
|
|
162
|
+
50 kB. [Benchmarks](benchmarks.md#limits-of-these-numbers) has the
|
|
163
|
+
measurements.
|