@geml/geml 1.8.3 → 1.8.4

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/dist/geml.js CHANGED
@@ -309,14 +309,32 @@ function matchMarker(line) {
309
309
  mk.start = parseInt(m[2], 10);
310
310
  return mk;
311
311
  }
312
+ // Leading indentation in COLUMNS (a tab counts as 4), the same measure the
313
+ // marker regex feeds into nesting decisions.
314
+ function indentColumns(line) {
315
+ let n = 0;
316
+ for (const ch of line) {
317
+ if (ch === " ")
318
+ n += 1;
319
+ else if (ch === "\t")
320
+ n += 4;
321
+ else
322
+ break;
323
+ }
324
+ return n;
325
+ }
312
326
  function makeListItem(mk, lineNo, ctx) {
313
327
  let text = interpolate(mk.rest, lineNo, ctx);
314
- // Task list item: a leading `[ ]` (open) or `[x]`/`[X]` (done) marker.
315
- const task = /^\[([ xX])\](?:[ \t]+(.*))?$/.exec(text);
328
+ // Task list item: a leading `[ ]` (open) or `[x]`/`[X]` (done) marker — on
329
+ // the item's FIRST line; continuation lines (§2.2) are already joined in,
330
+ // and a `[x]` there is content.
331
+ const nl = text.indexOf("\n");
332
+ const first = nl === -1 ? text : text.slice(0, nl);
333
+ const task = /^\[([ xX])\](?:[ \t]+(.*))?$/.exec(first);
316
334
  const item = { text, inlines: [] };
317
335
  if (task) {
318
336
  item.checked = task[1] !== " ";
319
- text = task[2] ?? "";
337
+ text = (task[2] ?? "") + (nl === -1 ? "" : text.slice(nl));
320
338
  item.text = text;
321
339
  }
322
340
  item.inlines = parseInline(text, lineNo, ctx);
@@ -380,9 +398,23 @@ function parseList(lines, i, base, ctx) {
380
398
  }
381
399
  if (prevBlank && cur.items.length > 0)
382
400
  cur.loose = true;
401
+ // §2.2 continuation lines: a non-blank line directly below the
402
+ // item, indented past its MARKER, that is neither an item line nor a `%%`
403
+ // comment, continues the item's inline content as a soft wrap — the same
404
+ // join a paragraph gives its lines. A blank line still ends the item, so
405
+ // multi-paragraph items remain outside the language.
406
+ let j = i + 1;
407
+ while (j < lines.length) {
408
+ const cand = lines[j];
409
+ const body = cand.trim();
410
+ if (body === "" || body.startsWith("%%") || matchMarker(cand) !== null || indentColumns(cand) <= mk.indent)
411
+ break;
412
+ mk.rest += "\n" + body;
413
+ j++;
414
+ }
383
415
  cur.items.push(makeListItem(mk, base + i + 1, ctx));
384
416
  prevBlank = false;
385
- i++;
417
+ i = j;
386
418
  }
387
419
  return { block: root, next: i };
388
420
  }
package/dist/serialize.js CHANGED
@@ -157,7 +157,13 @@ function serList(list, indent) {
157
157
  list.items.forEach((item, k) => {
158
158
  const marker = list.ordered ? `${start + k}. ` : "- ";
159
159
  const task = item.checked === undefined ? "" : item.checked ? "[x] " : "[ ] ";
160
- out.push(indent + marker + task + serInlines(item.inlines));
160
+ // A soft-wrapped item (§2.2) serializes as continuation lines aligned under
161
+ // its content, so the emission re-parses to the same item.
162
+ const [head, ...cont] = serInlines(item.inlines).split("\n");
163
+ out.push(indent + marker + task + head);
164
+ const contIndent = indent + " ".repeat(marker.length + task.length);
165
+ for (const l of cont)
166
+ out.push(contIndent + l);
161
167
  for (const child of item.children ?? []) {
162
168
  out.push(child.kind === "list" ? serList(child, indent + " ") : serBlock(child));
163
169
  }
package/dist/to-md.js CHANGED
@@ -116,7 +116,14 @@ function listToMd(b, indent, notes) {
116
116
  b.items.forEach((item, k) => {
117
117
  const marker = b.ordered ? `${start + k}. ` : "- ";
118
118
  const task = item.checked === undefined ? "" : item.checked ? "[x] " : "[ ] ";
119
- out.push(indent + marker + task + seq(item.inlines));
119
+ // A soft-wrapped item (§2.2) keeps its wrap: continuation lines indented to
120
+ // the content column, which both GFM and a GEML re-parse read as the same
121
+ // single item.
122
+ const [head, ...cont] = seq(item.inlines).split("\n");
123
+ out.push(indent + marker + task + head);
124
+ const contIndent = indent + " ".repeat(marker.length + task.length);
125
+ for (const l of cont)
126
+ out.push(contIndent + l);
120
127
  for (const child of item.children ?? []) {
121
128
  out.push(child.kind === "list" ? listToMd(child, indent + " ", notes) : block(child, notes));
122
129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geml/geml",
3
- "version": "1.8.3",
3
+ "version": "1.8.4",
4
4
  "mcpName": "io.github.geml-spec/geml",
5
5
  "publishConfig": {
6
6
  "access": "public"