mdrefine 0.2.2 → 0.2.3

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.
@@ -42,5 +42,7 @@ export interface MisreadLists {
42
42
  * not count. Renderers read the `===` below such text as text, of the list
43
43
  * item or after an empty one, so a paragraph or heading on the next line
44
44
  * continues it too, and a block there is nested: a `---` would underline it.
45
+ * Where such text ends a list item or block quote, renderers continue it
46
+ * lazily into a paragraph or heading on the next line below the container.
45
47
  */
46
48
  export declare function misreadLists(document: Document): MisreadLists;
@@ -23,6 +23,8 @@ const found = new WeakMap();
23
23
  * not count. Renderers read the `===` below such text as text, of the list
24
24
  * item or after an empty one, so a paragraph or heading on the next line
25
25
  * continues it too, and a block there is nested: a `---` would underline it.
26
+ * Where such text ends a list item or block quote, renderers continue it
27
+ * lazily into a paragraph or heading on the next line below the container.
26
28
  */
27
29
  export function misreadLists(document) {
28
30
  const known = found.get(document.tree);
@@ -58,16 +60,21 @@ export function misreadLists(document) {
58
60
  return node.children[0]?.position?.start.offset ?? start;
59
61
  return undefined;
60
62
  };
63
+ /** Returns the last line of misread text that ends `parent` and whose `===` renderers read as text. */
61
64
  const visit = (parent) => {
62
65
  let reach;
63
66
  /** The last line of misread text whose `===` renderers read as text, if the last block is that. */
64
67
  let continued;
68
+ /** Whether that text ends the last block, a container, rather than being it. */
69
+ let inside = false;
65
70
  parent.children.forEach((child, index) => {
66
71
  const offset = child.position?.start.offset;
67
72
  if (offset === undefined)
68
73
  return;
69
74
  const start = textStart(child);
70
- const adjacent = continued === child.position.start.line - 1;
75
+ // Renderers continue the text lazily on the line below the container
76
+ // that it ends, but only with text (issue #343).
77
+ const adjacent = continued === child.position.start.line - 1 && (!inside || start !== undefined);
71
78
  const misread = start !== undefined &&
72
79
  (listMarker.test(source.slice(start, start + 11)) || adjacent) &&
73
80
  !(index === 0 && parent.type === "listItem" && typeof parent.checked === "boolean");
@@ -88,9 +95,16 @@ export function misreadLists(document) {
88
95
  misread && child.type === "heading" && child.depth === 1
89
96
  ? child.position.end.line
90
97
  : undefined;
91
- if ("children" in child && !leaves.has(child.type))
92
- visit(child);
98
+ inside = false;
99
+ if ("children" in child && !leaves.has(child.type)) {
100
+ const last = visit(child);
101
+ if (last !== undefined && last === child.position.end.line) {
102
+ continued = last;
103
+ inside = true;
104
+ }
105
+ }
93
106
  });
107
+ return continued;
94
108
  };
95
109
  visit(document.tree);
96
110
  const result = { text, nested, next };
@@ -1 +1 @@
1
- {"version":3,"file":"misread-lists.js","sourceRoot":"","sources":["../../src/syntax/misread-lists.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Nodes, Parents, Root } from \"mdast\";\nimport type { Document } from \"../core/types.js\";\n\n/**\n * A list marker as CommonMark reads one at the start of a line's content: a\n * bullet, `-`, `*`, or `+`, or a number of one to nine digits followed by `.`\n * or `)`, and then a space, a tab, or the end of the line.\n */\nexport const listMarker = /^(?:[-+*]|\\d{1,9}[.)])(?:[ \\t\\r\\n]|$)/;\n\n/** Text that the parser read where renderers may read a list, and the blocks after it. */\nexport interface MisreadLists {\n /**\n * Paragraphs, and headings underlined with `===` or `---`, whose text starts\n * with a list marker, and the text that renderers read as continuing it.\n */\n text: ReadonlySet<Nodes>;\n /**\n * The blocks after such text in its container whose first lines start\n * further right than the text, up to one that does not. Renderers may read\n * them inside the list's last item, where their indentation counts from the\n * item's text: an indented code block can be a paragraph there.\n */\n nested: ReadonlySet<Nodes>;\n /**\n * The block after those, which starts no further right than the text.\n * Renderers may read it as more items of the list.\n */\n next: ReadonlySet<Nodes>;\n}\n\n/** Leaf blocks: their children, if they have any, are not blocks. */\nconst leaves = new Set<Nodes[\"type\"]>([\"paragraph\", \"heading\", \"table\", \"code\", \"html\"]);\nconst found = new WeakMap<Root, MisreadLists>();\n\n/**\n * Where renderers may read a list in text that the parser read as text.\n *\n * micromark refuses to start a list that does not start at 1, or an empty\n * item, where it would interrupt a paragraph, as CommonMark says, but also\n * where no paragraph is open: at the start of a list item, block quote, or\n * footnote definition below a paragraph, and after an indented code block\n * (issue #191). It reads the line as text there, where GitHub, commonmark.js,\n * and markdown-it read a list. By CommonMark's rules, text can start with a\n * list marker only below a link reference definition, as what remains of the\n * definition's lines; GitHub and commonmark.js read text there too, and\n * markdown-it a list. The paragraph of a task list item starts after its\n * checkbox, which renderers read as the start of the item's text, so it does\n * not count. Renderers read the `===` below such text as text, of the list\n * item or after an empty one, so a paragraph or heading on the next line\n * continues it too, and a block there is nested: a `---` would underline it.\n */\nexport function misreadLists(document: Document): MisreadLists {\n const known = found.get(document.tree);\n if (known) return known;\n const { source } = document;\n const text = new Set<Nodes>();\n const nested = new Set<Nodes>();\n const next = new Set<Nodes>();\n /** The column of the first character from `offset` on, after spaces and tabs. */\n const columnAt = (offset: number) => {\n let start = offset;\n while (start > 0 && source[start - 1] !== \"\\n\" && source[start - 1] !== \"\\r\") start--;\n let column = 0;\n for (let at = start; at < source.length; at++) {\n const character = source[at];\n if (at >= offset && character !== \" \" && character !== \"\\t\") break;\n column = character === \"\\t\" ? column + 4 - (column % 4) : column + 1;\n }\n return column;\n };\n /** Where a paragraph's or an underlined heading's text starts. */\n const textStart = (node: Nodes): number | undefined => {\n const start = node.position?.start.offset;\n if (start === undefined || node.position?.end.offset === undefined) return undefined;\n if (node.type === \"paragraph\") return start;\n // An underlined heading below link reference definitions starts at them.\n if (node.type === \"heading\" && source[start] !== \"#\")\n return node.children[0]?.position?.start.offset ?? start;\n return undefined;\n };\n const visit = (parent: Parents) => {\n let reach: number | undefined;\n /** The last line of misread text whose `===` renderers read as text, if the last block is that. */\n let continued: number | undefined;\n parent.children.forEach((child, index) => {\n const offset = child.position?.start.offset;\n if (offset === undefined) return;\n const start = textStart(child);\n const adjacent = continued === child.position!.start.line - 1;\n const misread =\n start !== undefined &&\n (listMarker.test(source.slice(start, start + 11)) || adjacent) &&\n !(index === 0 && parent.type === \"listItem\" && typeof parent.checked === \"boolean\");\n if (misread || adjacent || reach !== undefined) {\n const column = columnAt(misread ? start! : offset);\n if ((adjacent && !misread) || (reach !== undefined && column > reach)) nested.add(child);\n else if (reach !== undefined) {\n next.add(child);\n reach = undefined;\n }\n if (misread) {\n text.add(child);\n reach = Math.min(reach ?? column, column);\n }\n }\n continued =\n misread && child.type === \"heading\" && child.depth === 1\n ? child.position!.end.line\n : undefined;\n if (\"children\" in child && !leaves.has(child.type)) visit(child);\n });\n };\n visit(document.tree);\n const result = { text, nested, next };\n found.set(document.tree, result);\n return result;\n}\n"]}
1
+ {"version":3,"file":"misread-lists.js","sourceRoot":"","sources":["../../src/syntax/misread-lists.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Nodes, Parents, Root } from \"mdast\";\nimport type { Document } from \"../core/types.js\";\n\n/**\n * A list marker as CommonMark reads one at the start of a line's content: a\n * bullet, `-`, `*`, or `+`, or a number of one to nine digits followed by `.`\n * or `)`, and then a space, a tab, or the end of the line.\n */\nexport const listMarker = /^(?:[-+*]|\\d{1,9}[.)])(?:[ \\t\\r\\n]|$)/;\n\n/** Text that the parser read where renderers may read a list, and the blocks after it. */\nexport interface MisreadLists {\n /**\n * Paragraphs, and headings underlined with `===` or `---`, whose text starts\n * with a list marker, and the text that renderers read as continuing it.\n */\n text: ReadonlySet<Nodes>;\n /**\n * The blocks after such text in its container whose first lines start\n * further right than the text, up to one that does not. Renderers may read\n * them inside the list's last item, where their indentation counts from the\n * item's text: an indented code block can be a paragraph there.\n */\n nested: ReadonlySet<Nodes>;\n /**\n * The block after those, which starts no further right than the text.\n * Renderers may read it as more items of the list.\n */\n next: ReadonlySet<Nodes>;\n}\n\n/** Leaf blocks: their children, if they have any, are not blocks. */\nconst leaves = new Set<Nodes[\"type\"]>([\"paragraph\", \"heading\", \"table\", \"code\", \"html\"]);\nconst found = new WeakMap<Root, MisreadLists>();\n\n/**\n * Where renderers may read a list in text that the parser read as text.\n *\n * micromark refuses to start a list that does not start at 1, or an empty\n * item, where it would interrupt a paragraph, as CommonMark says, but also\n * where no paragraph is open: at the start of a list item, block quote, or\n * footnote definition below a paragraph, and after an indented code block\n * (issue #191). It reads the line as text there, where GitHub, commonmark.js,\n * and markdown-it read a list. By CommonMark's rules, text can start with a\n * list marker only below a link reference definition, as what remains of the\n * definition's lines; GitHub and commonmark.js read text there too, and\n * markdown-it a list. The paragraph of a task list item starts after its\n * checkbox, which renderers read as the start of the item's text, so it does\n * not count. Renderers read the `===` below such text as text, of the list\n * item or after an empty one, so a paragraph or heading on the next line\n * continues it too, and a block there is nested: a `---` would underline it.\n * Where such text ends a list item or block quote, renderers continue it\n * lazily into a paragraph or heading on the next line below the container.\n */\nexport function misreadLists(document: Document): MisreadLists {\n const known = found.get(document.tree);\n if (known) return known;\n const { source } = document;\n const text = new Set<Nodes>();\n const nested = new Set<Nodes>();\n const next = new Set<Nodes>();\n /** The column of the first character from `offset` on, after spaces and tabs. */\n const columnAt = (offset: number) => {\n let start = offset;\n while (start > 0 && source[start - 1] !== \"\\n\" && source[start - 1] !== \"\\r\") start--;\n let column = 0;\n for (let at = start; at < source.length; at++) {\n const character = source[at];\n if (at >= offset && character !== \" \" && character !== \"\\t\") break;\n column = character === \"\\t\" ? column + 4 - (column % 4) : column + 1;\n }\n return column;\n };\n /** Where a paragraph's or an underlined heading's text starts. */\n const textStart = (node: Nodes): number | undefined => {\n const start = node.position?.start.offset;\n if (start === undefined || node.position?.end.offset === undefined) return undefined;\n if (node.type === \"paragraph\") return start;\n // An underlined heading below link reference definitions starts at them.\n if (node.type === \"heading\" && source[start] !== \"#\")\n return node.children[0]?.position?.start.offset ?? start;\n return undefined;\n };\n /** Returns the last line of misread text that ends `parent` and whose `===` renderers read as text. */\n const visit = (parent: Parents): number | undefined => {\n let reach: number | undefined;\n /** The last line of misread text whose `===` renderers read as text, if the last block is that. */\n let continued: number | undefined;\n /** Whether that text ends the last block, a container, rather than being it. */\n let inside = false;\n parent.children.forEach((child, index) => {\n const offset = child.position?.start.offset;\n if (offset === undefined) return;\n const start = textStart(child);\n // Renderers continue the text lazily on the line below the container\n // that it ends, but only with text (issue #343).\n const adjacent =\n continued === child.position!.start.line - 1 && (!inside || start !== undefined);\n const misread =\n start !== undefined &&\n (listMarker.test(source.slice(start, start + 11)) || adjacent) &&\n !(index === 0 && parent.type === \"listItem\" && typeof parent.checked === \"boolean\");\n if (misread || adjacent || reach !== undefined) {\n const column = columnAt(misread ? start! : offset);\n if ((adjacent && !misread) || (reach !== undefined && column > reach)) nested.add(child);\n else if (reach !== undefined) {\n next.add(child);\n reach = undefined;\n }\n if (misread) {\n text.add(child);\n reach = Math.min(reach ?? column, column);\n }\n }\n continued =\n misread && child.type === \"heading\" && child.depth === 1\n ? child.position!.end.line\n : undefined;\n inside = false;\n if (\"children\" in child && !leaves.has(child.type)) {\n const last = visit(child);\n if (last !== undefined && last === child.position!.end.line) {\n continued = last;\n inside = true;\n }\n }\n });\n return continued;\n };\n visit(document.tree);\n const result = { text, nested, next };\n found.set(document.tree, result);\n return result;\n}\n"]}
package/docs/design.md CHANGED
@@ -52,14 +52,15 @@ The comparison is only as good as the parser's reading:
52
52
  there are left out, plugins' too, and so are their findings. The list rules
53
53
  also leave a list there, or directly after it, as written, because another
54
54
  marker or indentation could join it to that list or move it out of the list,
55
- and `style/block-spacing` adds no blank line that would make the list loose.
56
- The same holds below a link reference definition, where the parser and GitHub
57
- read such a line as text and markdown-it reads a list. Below an empty list
58
- item (`-`), the parser takes the `[x]` that begins the next line for a
59
- checkbox that belongs to no item, where GitHub shows the text `[x]`, so a
60
- blank line between them, as `style/block-spacing` would add, is refused as a
61
- change of meaning. The parser also keeps the spaces and tabs that start a code
62
- span's next line, where CommonMark and GitHub leave them out, so that a span
55
+ and `style/block-spacing` adds no blank line that would make the list loose,
56
+ or that would end text that renderers continue on the line below the list. The
57
+ same holds below a link reference definition, where the parser and GitHub read
58
+ such a line as text and markdown-it reads a list. Below an empty list item
59
+ (`-`), the parser takes the `[x]` that begins the next line for a checkbox
60
+ that belongs to no item, where GitHub shows the text `[x]`, so a blank line
61
+ between them, as `style/block-spacing` would add, is refused as a change of
62
+ meaning. The parser also keeps the spaces and tabs that start a code span's
63
+ next line, where CommonMark and GitHub leave them out, so that a span
63
64
  `npm run` continued by an indented `build` reads `npm run build`, and it
64
65
  takes a space or line ending off each end of a span before its line endings
65
66
  become spaces, where CommonMark takes spaces off after. The comparison reads a
@@ -73,7 +74,12 @@ The comparison is only as good as the parser's reading:
73
74
  lines of another table as text, where GitHub shows a table, so `style/wrap`
74
75
  and `style/block-spacing` leave text whose second line is a table delimiter
75
76
  row as written ([issue
76
- #283](https://github.com/viell-dev/slop-markdown-tools/issues/283)).
77
+ #283](https://github.com/viell-dev/slop-markdown-tools/issues/283)). A fenced
78
+ code block without its closing fence runs to the end of its list item, and
79
+ CommonMark and GitHub count the blank lines at the end of the item as code,
80
+ where the parser does not, so `style/block-spacing` adds no blank line below
81
+ such a list ([issue
82
+ #350](https://github.com/viell-dev/slop-markdown-tools/issues/350)).
77
83
  - Front matter needs a closing line. A first line of `---` or `+++` that no
78
84
  later line closes is read as renderers read it, as a thematic break or text,
79
85
  with the lists and block quotes below it; the parser's front matter extension
@@ -6,7 +6,7 @@ mdrefine needs Node.js 22.12 or newer. Install the package in the project that
6
6
  holds your documents:
7
7
 
8
8
  ```sh
9
- npm install --save-dev --save-exact mdrefine@0.2.2
9
+ npm install --save-dev --save-exact mdrefine@0.2.3
10
10
  npx --no-install mdtools --help
11
11
  ```
12
12
 
package/docs/rules.md CHANGED
@@ -245,7 +245,9 @@ label shows its target as text, and a note embed is not the same thing as a
245
245
  Markdown image. A new wikilink decodes percent escapes, because Obsidian takes
246
246
  its text as the note's name: `[x](My%20Note.md)` becomes `[[My Note.md|x]]`. A
247
247
  destination that does not decode, or whose name would hold `|`, `[`, `]`, `#`,
248
- or `^`, stays as written.
248
+ or `^`, stays as written. In a table cell, a `|` that the rule writes, between a
249
+ new wikilink's target and label or in a new Markdown label, is escaped as `\|`,
250
+ because one that is not escaped ends the cell.
249
251
 
250
252
  With `"style": "markdown"`, before:
251
253
 
@@ -293,11 +295,14 @@ only when the new destination leads to the same file and fragment.
293
295
  A relative path also gets `./` when it would otherwise name a different file at
294
296
  the vault's root. A destination that already has the configured spelling keeps
295
297
  its percent-encoding; a new path encodes `%` and `#`, so that it names the same
296
- file. Absolute filesystem paths are never written. These are left as written: a
297
- wikilink without a label, whose target is also its visible text; reference
298
- definitions, which are checked but not rewritten; autolinks; and links whose
299
- target is uncertain because a directory could not be read or the workspace root
300
- is only part of a vault (see [Obsidian
298
+ file, and in a table cell writes a `|` in a name as `\|`, because one that is
299
+ not escaped ends the cell. In a wikilink only the destination changes: the `|`
300
+ or `\|` after it, the label, and an embed's size stay as written. Absolute
301
+ filesystem paths are never written. These are left as written: a wikilink
302
+ without a label, whose target is also its visible text; reference definitions,
303
+ which are checked but not rewritten; autolinks; and links whose target is
304
+ uncertain because a directory could not be read or the workspace root is only
305
+ part of a vault (see [Obsidian
301
306
  vaults](obsidian.md#a-workspace-root-inside-a-vault)).
302
307
 
303
308
  With `{ "style": "relative", "leadingDot": true }` in `README.md`, before:
@@ -677,7 +682,9 @@ or what a comment applies to:
677
682
  blocks of a list item makes the list loose, which changes how it renders.
678
683
  - Front matter and the block below it.
679
684
  - A list that ends inside an HTML comment or another HTML block without its end
680
- marker, which would take the blank line in.
685
+ marker, which would take the blank line in. The same goes for a list whose
686
+ last item ends in a fenced code block without its closing fence: renderers put
687
+ the blank line into the code, although the parser does not.
681
688
  - A heading or list directly below an HTML comment or below a
682
689
  `mdtools-disable-next-line` directive, which apply to the line below them.
683
690
  - The line that opens a callout or alert, such as `> [!NOTE]`, and the block
@@ -691,7 +698,9 @@ or what a comment applies to:
691
698
  - Text that the parser reads where renderers read a list, such as `- 2. Step`
692
699
  directly below text: the list or quote that starts with it, the blocks
693
700
  indented below it, and a list after them, which renderers read as part of that
694
- list (see [how it works](design.md#safety-model)).
701
+ list, and text on the line directly below the list item or quote that it ends,
702
+ which renderers read as more of it (see [how it
703
+ works](design.md#safety-model)).
695
704
  - Directly below a table that ends in a list item or block quote: text whose
696
705
  second line is a table delimiter row. The parser reads a table there as text,
697
706
  where GitHub reads a table, and a blank line would make it a table for the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdrefine",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Configurable Markdown linting and formatting for CommonMark, GitHub, Forgejo, Gitea, and Obsidian",
5
5
  "keywords": [
6
6
  "markdown",