@pretextbook/markdown-style-pretext 0.1.4 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @pretextbook/markdown-style-pretext
2
2
 
3
- Editor intelligence for **Markdown-style PreTeXt** — CommonMark + GFM plus
3
+ Editor intelligence for **Markdown-style PreTeXt** — CommonMark + GFM tables plus
4
4
  [remark-directive](https://github.com/remarkjs/remark-directive) extensions that convert to
5
5
  PreTeXt via [`@pretextbook/remark-pretext`](../remark-pretext).
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretextbook/markdown-style-pretext",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Editor intelligence (completions + validation) for Markdown-style PreTeXt documents",
5
5
  "author": "Oscar Levin",
6
6
  "license": "MIT",
@@ -34,11 +34,11 @@
34
34
  "test": "vitest run"
35
35
  },
36
36
  "dependencies": {
37
- "@pretextbook/latex-style-pretext": "^0.5.0",
37
+ "@pretextbook/latex-style-pretext": "^0.6.0",
38
38
  "vscode-languageserver-types": "^3.17.5"
39
39
  },
40
40
  "devDependencies": {
41
- "@pretextbook/remark-pretext": "^0.1.0"
41
+ "@pretextbook/remark-pretext": "^0.4.0"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public"
@@ -23,6 +23,13 @@ describe("container directive completions", () => {
23
23
  expect(item?.textEdit?.newText).toBe("theorem\n\t$0\n:::");
24
24
  });
25
25
 
26
+ it("gives :::table a title placeholder and a pipe-table body", () => {
27
+ const item = byLabel(complete(":::tab|"), "table");
28
+ expect(item?.textEdit?.newText).toBe(
29
+ "table[${1:Title}]\n| ${2:Header} | ${3:Header} |\n| --- | --- |\n| $4 | $0 |\n:::",
30
+ );
31
+ });
32
+
26
33
  it("seeds a nested :::task for task-bearing directives", () => {
27
34
  const item = byLabel(complete(":::exerc|"), "exercise");
28
35
  expect(item?.textEdit?.newText).toContain(":::task");
@@ -106,6 +113,11 @@ describe("cross-reference completions", () => {
106
113
  const items = complete(":::theorem{#thm:main}\nHi\n:::\n\nSee [it](#thm|");
107
114
  expect(labels(items)).toContain("thm:main");
108
115
  });
116
+
117
+ it("completes an empty `[](#…` link, and ids on headings", () => {
118
+ const items = complete("## Intro {#sec:intro}\n\nSee [](#sec|");
119
+ expect(labels(items)).toContain("sec:intro");
120
+ });
109
121
  });
110
122
 
111
123
  describe("suppression", () => {
@@ -11,11 +11,17 @@ import type { DirectiveSpec } from "../types";
11
11
  * nested tasks seed a first `:::task` child so the structure is discoverable.
12
12
  */
13
13
  export function containerInsertText(spec: DirectiveSpec): string {
14
- if (spec.snippet) return `${spec.name}\n${spec.snippet}\n:::`;
14
+ const head = nameLine(spec);
15
+ if (spec.snippet) return `${head}\n${spec.snippet}\n:::`;
15
16
  if (spec.hasNestedTasks) {
16
- return `${spec.name}\n:::task\n\t$0\n:::\n:::`;
17
+ return `${head}\n:::task\n\t$0\n:::\n:::`;
17
18
  }
18
- return `${spec.name}\n\t$0\n:::`;
19
+ return `${head}\n\t$0\n:::`;
20
+ }
21
+
22
+ /** The directive name, plus a title placeholder when it needs one. */
23
+ function nameLine(spec: DirectiveSpec): string {
24
+ return spec.titled ? `${spec.name}[\${1:Title}]` : spec.name;
19
25
  }
20
26
 
21
27
  /** The bare closing fence, offered when a container directive is open. */
@@ -31,10 +37,15 @@ export function closeInsertText(): string {
31
37
  * Task-bearing directives seed a nested `task:` child.
32
38
  */
33
39
  export function pythonInsertText(spec: DirectiveSpec): string {
40
+ const head = `${nameLine(spec)}:`;
41
+ if (spec.snippet) {
42
+ const body = spec.snippet.split("\n").map((line) => `\t${line}`);
43
+ return [head, ...body].join("\n");
44
+ }
34
45
  if (spec.hasNestedTasks) {
35
- return `${spec.name}:\n\ttask:\n\t\t$0`;
46
+ return `${head}\n\ttask:\n\t\t$0`;
36
47
  }
37
- return `${spec.name}:\n\t$0`;
48
+ return `${head}\n\t$0`;
38
49
  }
39
50
 
40
51
  /**
@@ -11,8 +11,8 @@
11
11
  // - LEAF directives (`::name{…}`) are the PreTeXt Plus *include* syntax; the
12
12
  // converter turns any `::name` into `<plus:name/>`, so no leaf name is ever
13
13
  // "unknown". `LEAF_DIRECTIVES` below is only a convenience completion list.
14
- // - TEXT directives (`:name[…]`) have no converter handler yet (they become
15
- // `<TODO>` placeholders), so this package neither completes nor lints them.
14
+ // - TEXT directives (`:name[…]`) have no converter handler yet (they stay
15
+ // text), so this package neither completes nor lints them.
16
16
 
17
17
  import { PLUS_TYPES, PLUS_TYPE_KIND } from "@pretextbook/latex-style-pretext";
18
18
  import type { DirectiveSpec, DirectiveCategory } from "../types";
@@ -23,6 +23,8 @@ type ContainerSeed = {
23
23
  requiresStatement: boolean;
24
24
  hasNestedTasks?: boolean;
25
25
  documentation?: string;
26
+ snippet?: string;
27
+ titled?: boolean;
26
28
  };
27
29
 
28
30
  const CONTAINER_SEEDS: Record<string, ContainerSeed> = {
@@ -82,6 +84,17 @@ const CONTAINER_SEEDS: Record<string, ContainerSeed> = {
82
84
  solution: { category: "solution-like", requiresStatement: false },
83
85
  hint: { category: "solution-like", requiresStatement: false },
84
86
  answer: { category: "solution-like", requiresStatement: false },
87
+
88
+ // figure-like (a titled wrapper around one piece of content)
89
+ table: {
90
+ category: "figure-like",
91
+ requiresStatement: false,
92
+ titled: true,
93
+ snippet: "| ${2:Header} | ${3:Header} |\n| --- | --- |\n| $4 | $0 |",
94
+ documentation:
95
+ "A numbered, titled `<table>` around a pipe table (`<tabular>`). " +
96
+ "A pipe table on its own, outside `:::table`, is a bare `<tabular>`.",
97
+ },
85
98
  };
86
99
 
87
100
  /**
@@ -125,6 +138,8 @@ function seedToSpec(name: string, seed: ContainerSeed): DirectiveSpec {
125
138
  hasNestedTasks: seed.hasNestedTasks,
126
139
  childDirectives: children.length > 0 ? children : undefined,
127
140
  documentation: seed.documentation,
141
+ snippet: seed.snippet,
142
+ titled: seed.titled,
128
143
  };
129
144
  }
130
145
 
@@ -23,6 +23,15 @@ describe("directive fence matching", () => {
23
23
  expect(diags[0].message).toContain("no matching open");
24
24
  });
25
25
 
26
+ it("accepts pipe tables, bare and in a :::table", () => {
27
+ expect(
28
+ getMarkdownDiagnostics(
29
+ "| A | $|x|$ |\n| - | - |\n| 1 | 2 |\n\n" +
30
+ ":::table[Results]{#tbl-res}\n| A |\n| - |\n| 1 |\n:::\n",
31
+ ),
32
+ ).toEqual([]);
33
+ });
34
+
26
35
  it("accepts nested containers written with flexible equal colons", () => {
27
36
  expect(
28
37
  getMarkdownDiagnostics(":::exercise\n:::task\nx\n:::\n:::\n"),
package/src/types.ts CHANGED
@@ -14,9 +14,9 @@ export type { PretextFlavorLanguage, GetCompletionsParams };
14
14
  * - `container` — `:::name` … `:::` (block), the workhorse (theorem, exercise…)
15
15
  * - `leaf` — `::name{…}` (a self-closing block; the PreTeXt Plus include
16
16
  * syntax, so *any* name is accepted and none is ever "unknown")
17
- * - `text` — `:name[…]{…}` (inline). The converter has no `textDirective`
18
- * handler yet, so these are recognized by the scanner but neither completed
19
- * nor linted (see the note in ./data/directives.ts).
17
+ * - `text` — `:name[…]{…}` (inline). The converter doesn't read these yet
18
+ * and leaves them as text, so they are recognized by the scanner but neither
19
+ * completed nor linted (see the note in ./data/directives.ts).
20
20
  */
21
21
  export type DirectiveKind = "container" | "leaf" | "text";
22
22
 
@@ -34,7 +34,8 @@ export type DirectiveCategory =
34
34
  | "example-like"
35
35
  | "project-like"
36
36
  | "exercise-like"
37
- | "solution-like";
37
+ | "solution-like"
38
+ | "figure-like";
38
39
 
39
40
  /**
40
41
  * Curated specification for a container directive. The direct analogue of the
@@ -68,8 +69,14 @@ export interface DirectiveSpec {
68
69
  /**
69
70
  * Override the auto-generated snippet body (LSP snippet syntax). The
70
71
  * generator always supplies the `name` line and the closing `:::` fence, so
71
- * this is the inner body only.
72
+ * this is the inner body only. Number its tab stops from `$2` when the
73
+ * spec is `titled`, since the title placeholder takes `$1`.
72
74
  */
73
75
  snippet?: string;
76
+ /**
77
+ * The PreTeXt element needs a `<title>` (e.g. `table`), so the generated
78
+ * name line carries a `[Title]` placeholder.
79
+ */
80
+ titled?: boolean;
74
81
  documentation?: string;
75
82
  }