rapier-markdown-kit 1.2.3 → 1.2.5
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/LICENSE +3 -2
- package/README.md +16 -4
- package/dist/images/gif.mjs +3 -1
- package/dist/images/playback.mjs +6 -4
- package/dist/kit/conformance/run.mjs +1 -1
- package/dist/kit/ledger/authored-origin.mjs +49 -0
- package/dist/kit/ledger/authorship.mjs +18 -9
- package/dist/kit/ledger/data.mjs +7 -4
- package/dist/kit/ledger/format.mjs +25 -8
- package/dist/kit/ledger/history.mjs +31 -5
- package/dist/kit/ledger/inverse-source.mjs +111 -49
- package/dist/kit/ledger/journal-records.mjs +24 -29
- package/dist/kit/ledger/merge.mjs +48 -13
- package/dist/kit/ledger/text-merge.mjs +75 -29
- package/dist/kit/ledger/transport.mjs +30 -45
- package/dist/kit/pdf-vectors.mjs +2 -1
- package/dist/kit/print-capitals.mjs +0 -1
- package/dist/kit/rapier-ledger.mjs +2 -0
- package/dist/kit/render-markdown.mjs +10 -1
- package/dist/kit/render-print.mjs +43 -7
- package/dist/kit/render-sanitize.mjs +19 -12
- package/dist/kit/render-styles.mjs +3 -3
- package/dist/kit/render.mjs +5 -40
- package/dist/layout/paragraph-fit.mjs +1 -15
- package/dist/layout/typography.mjs +2 -1
- package/dist/packages/rapier-diff/concerns.mjs +163 -0
- package/dist/packages/rapier-diff/diff.mjs +68 -0
- package/dist/packages/rapier-diff/index.mjs +4 -744
- package/dist/packages/rapier-diff/matching.mjs +157 -0
- package/dist/packages/rapier-diff/merge.mjs +226 -0
- package/dist/packages/rapier-diff/moves.mjs +130 -0
- package/dist/packages/rapier-diff/structure.mjs +50 -0
- package/dist/packages/rapier-diff/tokens.mjs +72 -0
- package/dist/packages/rapier-diff/walk.mjs +219 -0
- package/dist/spec/html-blocks.mjs +3 -3
- package/dist/spec/md-layout.mjs +1 -1
- package/dist/spec/md-marks.mjs +1 -1
- package/dist/spec/md-source.mjs +15 -7
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {words, letter, within, prev, next} from './tokens.mjs';
|
|
3
|
+
|
|
4
|
+
// Exact source records for a range: lines, then words, then characters, or structural parts when supplied.
|
|
5
|
+
export function walker(c) {
|
|
6
|
+
const {before, after, spend, safe, budget, raw, chars, code, segmenter, tokens, matches} = c;
|
|
7
|
+
function record(a0, a1, b0, b1, unit, part, coarse = false) {
|
|
8
|
+
if (a1 - a0 === b1 - b0 && before.slice(a0, a1) === after.slice(b0, b1)) return;
|
|
9
|
+
// Internal records: before, after, coarse, concern, transported fragments.
|
|
10
|
+
c.result.push([[a0, a1], [b0, b1], coarse, [a0 === a1 ? 2 : b0 === b1 ? 0 : 1, part?.kind || unit, part?.name]]);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// A line deletion or insertion holding whole named parts and only blank text besides is one record per part,
|
|
14
|
+
// cut at line starts, so each keeps its own name (and its callers' breaks).
|
|
15
|
+
function name(from, ownersA, ownersB) {
|
|
16
|
+
for (let r = from; r < c.result.length; r++) {
|
|
17
|
+
const [[a0, a1], [b0, b1]] = c.result[r], side = a0 === a1 ? 1 : b0 === b1 ? 0 : -1;
|
|
18
|
+
if (side < 0) continue;
|
|
19
|
+
const [s0, s1] = c.result[r][side], text = side ? after : before;
|
|
20
|
+
const parts = (side ? ownersB : ownersA).filter(part => part?.name && part.start >= s0 && part.end <= s1);
|
|
21
|
+
if (!parts.length || !spend(s1 - s0)) continue;
|
|
22
|
+
let blank = text.slice(s0, parts[0].start);
|
|
23
|
+
for (let k = 1; k <= parts.length; k++) blank += text.slice(parts[k - 1].end, parts[k]?.start ?? s1);
|
|
24
|
+
if (/\S/.test(blank)) continue;
|
|
25
|
+
const rows = parts.map((part, k) => {
|
|
26
|
+
const start = k ? Math.max(parts[k - 1].end, text.lastIndexOf('\n', part.start - 1) + 1) : s0;
|
|
27
|
+
return [start, part];
|
|
28
|
+
});
|
|
29
|
+
c.result.splice(r, 1, ...rows.map(([start, part], k) => {
|
|
30
|
+
const end = rows[k + 1]?.[0] ?? s1, here = [side ? [a0, a0] : [start, end], side ? [start, end] : [b0, b0]];
|
|
31
|
+
return [here[0], here[1], false, [side ? 2 : 0, part.kind, part.name]];
|
|
32
|
+
}));
|
|
33
|
+
r += rows.length - 1;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A search step scans at most one fixed-size source chunk.
|
|
38
|
+
function common(a, b, end, step) {
|
|
39
|
+
let n = 0;
|
|
40
|
+
while (n < end && spend()) {
|
|
41
|
+
const stop = Math.min(end, n + 256);
|
|
42
|
+
while (n < stop && before.charCodeAt(a) === after.charCodeAt(b)) { n++; a += step; b += step; }
|
|
43
|
+
if (n < stop) break;
|
|
44
|
+
}
|
|
45
|
+
return n;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function walk(a0, a1, b0, b1, level, ap, bp, context) {
|
|
49
|
+
if (a1 - a0 === b1 - b0 && before.slice(a0, a1) === after.slice(b0, b1)) return;
|
|
50
|
+
if (!spend()) { record(a0, a1, b0, b1, 'block', context, true); return; }
|
|
51
|
+
const structural = level <= 0 && level > -128 && (ap?.length || bp?.length);
|
|
52
|
+
if (!structural && raw !== 'word') {
|
|
53
|
+
let n = common(a0, b0, Math.min(a1 - a0, b1 - b0), 1);
|
|
54
|
+
while (n && (!safe(before, a0 + n) || !safe(after, b0 + n) || !raw && level < 3 && (within(before, a0 + n) || within(after, b0 + n)))) {
|
|
55
|
+
if (!spend()) { n = 0; break; } n--;
|
|
56
|
+
}
|
|
57
|
+
if (level === 1 && n) {
|
|
58
|
+
const next = Math.max(0, before.lastIndexOf('\n', a0 + n - 1) + 1 - a0);
|
|
59
|
+
spend(Math.ceil((n - next) / 256)); n = next;
|
|
60
|
+
}
|
|
61
|
+
a0 += n; b0 += n;
|
|
62
|
+
n = common(a1 - 1, b1 - 1, Math.min(a1 - a0, b1 - b0), -1);
|
|
63
|
+
while (n && (!safe(before, a1 - n) || !safe(after, b1 - n) || !raw && level < 3 && (within(before, a1 - n) || within(after, b1 - n)))) {
|
|
64
|
+
if (!spend()) { n = 0; break; } n--;
|
|
65
|
+
}
|
|
66
|
+
if (level === 1) while (n && (before[a1 - n - 1] !== '\n' || after[b1 - n - 1] !== '\n' || before[a1 - n] === '\n' || after[b1 - n] === '\n')) { if (!spend()) { n = 0; break; } n--; }
|
|
67
|
+
a1 -= n; b1 -= n;
|
|
68
|
+
if (budget.left < 0) { record(a0, a1, b0, b1, 'block', context, true); return; }
|
|
69
|
+
}
|
|
70
|
+
let unit = level >= 3 ? 'char' : level === 2 ? 'word' : 'line';
|
|
71
|
+
if (!raw && level === 2 && a1 - a0 + b1 - b0 <= 4 && [...before.slice(a0, a1), ...after.slice(b0, b1)].length <= 2 &&
|
|
72
|
+
letter(prev(before, a0) || prev(after, b0)) && letter(next(before, a1) || next(after, b1))) unit = 'char';
|
|
73
|
+
if (!structural && (a0 === a1 || b0 === b1)) { record(a0, a1, b0, b1, unit, context); return; }
|
|
74
|
+
const left = tokens(before, a0, a1, structural ? 0 : Math.max(1, level), ap);
|
|
75
|
+
const right = tokens(after, b0, b1, structural ? 0 : Math.max(1, level), bp);
|
|
76
|
+
if (!left || !right) { record(a0, a1, b0, b1, unit, context, true); return; }
|
|
77
|
+
const [a, x, ownersA] = left, [b, y, ownersB] = right, pairs = matches(a, b);
|
|
78
|
+
let ai = 0, bi = 0;
|
|
79
|
+
for (let i = 0; i <= a.length; i++) {
|
|
80
|
+
const j = i === a.length ? b.length : pairs[i];
|
|
81
|
+
if (j === undefined) continue;
|
|
82
|
+
const x0 = x[ai], x1 = x[i], y0 = y[bi], y1 = y[j];
|
|
83
|
+
if (ai < i || bi < j) {
|
|
84
|
+
if (budget.left < 0 || pairs.coarse?.has(ai)) record(x0, x1, y0, y1, unit, context, true);
|
|
85
|
+
else if (structural && (ai === i || bi === j)) {
|
|
86
|
+
for (let k = ai; k < i; k++) record(x[k], x[k + 1], y0, y0, unit, ownersA[k] || context);
|
|
87
|
+
for (let k = bi; k < j; k++) record(x1, x1, y[k], y[k + 1], unit, ownersB[k] || context);
|
|
88
|
+
} else if (structural) {
|
|
89
|
+
const p = ownersA[ai], q = ownersB[bi];
|
|
90
|
+
if (i - ai === 1 && j - bi === 1 && p && q && p.kind === q.kind) walk(x0, x1, y0, y1, level - 1, p.children, q.children, q);
|
|
91
|
+
else {
|
|
92
|
+
const from = c.result.length;
|
|
93
|
+
walk(x0, x1, y0, y1, 1, null, null, context);
|
|
94
|
+
name(from, ownersA.slice(ai, i), ownersB.slice(bi, j));
|
|
95
|
+
}
|
|
96
|
+
} else if (level < 2 && raw !== 'line') lines(x.slice(ai, i + 1), y.slice(bi, j + 1), context);
|
|
97
|
+
else if (level === 2 && chars && !raw && i - ai <= 1 && j - bi <= 1 && x1 - x0 + y1 - y0 < 100) {
|
|
98
|
+
// A changed token is replaced whole unless the caller asks for the characters inside it.
|
|
99
|
+
walk(x0, x1, y0, y1, 3, null, null, context);
|
|
100
|
+
} else record(x0, x1, y0, y1, unit, context);
|
|
101
|
+
}
|
|
102
|
+
if (i < a.length) {
|
|
103
|
+
const p = ownersA[i], q = ownersB[j];
|
|
104
|
+
if ((p || q || level <= 1 && !raw) && before.slice(x[i], x[i + 1]) !== after.slice(y[j], y[j + 1]))
|
|
105
|
+
walk(x[i], x[i + 1], y[j], y[j + 1], p || q ? level - 1 : 3, p?.children, q?.children, q || p || context);
|
|
106
|
+
}
|
|
107
|
+
ai = i + 1; bi = j + 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Unequal lines pair in order by shared words. A line sharing under a quarter of its words with every candidate is
|
|
112
|
+
// replaced whole: an inserted line never borrows a neighbour's words, and a rewrite never reads as stray fragments.
|
|
113
|
+
function lines(x, y, context) {
|
|
114
|
+
const n = x.length - 1, m = y.length - 1;
|
|
115
|
+
if (n * m > 4096 || !spend(n * m + x[n] - x[0] + y[m] - y[0])) return row(x[0], x[n], y[0], y[m], context);
|
|
116
|
+
// A renamed name's sites count as shared words.
|
|
117
|
+
if (!c.renames) {
|
|
118
|
+
c.renames = [[], []];
|
|
119
|
+
for (const [p, old] of c.aliases || []) if (before.slice(...old.nameAt) !== after.slice(...p.nameAt)) {
|
|
120
|
+
c.renames[0].push(old.nameAt, ...c.la.filter(l => l.key === old.key).map(l => l.at));
|
|
121
|
+
c.renames[1].push(p.nameAt, ...c.lb.filter(l => l.key === p.key).map(l => l.at));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const weight = (side, a, b) => {
|
|
125
|
+
const counts = new Map(), text = side ? after : before;
|
|
126
|
+
for (const [c, d] of words(segmenter, text.slice(a, b))) counts.set(text.slice(a + c, a + d), (counts.get(text.slice(a + c, a + d)) || 0) + d - c);
|
|
127
|
+
return [counts, [...counts.values()].reduce((n, v) => n + v, 0), c.renames[side].reduce((n, at) => n + Math.max(0, Math.min(b, at[1]) - Math.max(a, at[0])), 0)];
|
|
128
|
+
};
|
|
129
|
+
const left = x.slice(0, -1).map((at, k) => weight(0, at, x[k + 1])), right = y.slice(0, -1).map((at, k) => weight(1, at, y[k + 1]));
|
|
130
|
+
const best = Array.from({length: n + 1}, () => new Float64Array(m + 1)), pairs = [];
|
|
131
|
+
for (let k = 1; k <= n; k++) for (let l = 1; l <= m; l++) {
|
|
132
|
+
const [a, sa, ra] = left[k - 1], [b, sb, rb] = right[l - 1];
|
|
133
|
+
let shared = sa || sb ? Math.max(ra, rb) : 0.5;
|
|
134
|
+
for (const [key, v] of a) shared += Math.min(v, b.get(key) || 0);
|
|
135
|
+
best[k][l] = Math.max(best[k - 1][l], best[k][l - 1], shared * 4 > Math.max(sa, sb) && best[k - 1][l - 1] + shared);
|
|
136
|
+
}
|
|
137
|
+
for (let k = n, l = m; k && l;) {
|
|
138
|
+
if (best[k][l] === best[k - 1][l]) k--;
|
|
139
|
+
else if (best[k][l] === best[k][l - 1]) l--;
|
|
140
|
+
else pairs.unshift([--k, --l]);
|
|
141
|
+
}
|
|
142
|
+
let k = 0, l = 0;
|
|
143
|
+
for (const [p, q] of [...pairs, [n, m]]) {
|
|
144
|
+
// A merge compares one line against one line by character, whatever they share.
|
|
145
|
+
if (raw === 'char' && p - k === 1 && q - l === 1) row(x[k], x[p], y[l], y[q], context);
|
|
146
|
+
else if (k < p || l < q) record(x[k], x[p], y[l], y[q], 'line', context);
|
|
147
|
+
if (p < n) row(x[p], x[p + 1], y[q], y[q + 1], context);
|
|
148
|
+
k = p + 1; l = q + 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// A table row with the same number of cells on both sides changes cell by cell; a character diff stays in its line.
|
|
153
|
+
function row(a0, a1, b0, b1, context) {
|
|
154
|
+
if (raw === 'char') return walk(a0, a1, b0, b1, 3, null, null, context);
|
|
155
|
+
const cells = (text, a, b) => /^[ \t]*\|/.test(text.slice(a, b)) && [...text.slice(a, b).matchAll(/(?<!\\)\|/g)].map(m => a + m.index);
|
|
156
|
+
const p = !code && cells(before, a0, a1), q = p && cells(after, b0, b1);
|
|
157
|
+
if (!q || p.length !== q.length || !spend(p.length)) return walk(a0, a1, b0, b1, 2, null, null, context);
|
|
158
|
+
for (let k = 0; k <= p.length; k++) walk(k ? p[k - 1] + 1 : a0, p[k] ?? a1, k ? q[k - 1] + 1 : b0, q[k] ?? b1, 2, null, null, context);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Surviving source pieces keep the occurrence witnessed by the host.
|
|
162
|
+
function operations(rows) {
|
|
163
|
+
if (!Array.isArray(rows)) return null;
|
|
164
|
+
const pieces = [[0, before.length]];
|
|
165
|
+
let length = before.length, steps = 1e7;
|
|
166
|
+
const verify = n => {
|
|
167
|
+
if ((steps -= n) >= 0) return true;
|
|
168
|
+
budget.left = -1; return false;
|
|
169
|
+
};
|
|
170
|
+
const split = at => {
|
|
171
|
+
let position = 0;
|
|
172
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
173
|
+
const [start, end, text] = pieces[i], size = end - start;
|
|
174
|
+
if (position === at) return i;
|
|
175
|
+
if (position + size > at) {
|
|
176
|
+
const n = at - position;
|
|
177
|
+
pieces.splice(i, 1, [start, start + n, text], [start + n, end, text]);
|
|
178
|
+
return i + 1;
|
|
179
|
+
}
|
|
180
|
+
position += size;
|
|
181
|
+
}
|
|
182
|
+
return pieces.length;
|
|
183
|
+
};
|
|
184
|
+
for (const row of rows) {
|
|
185
|
+
if (!row || !Number.isSafeInteger(row.pos) || typeof row.removed !== 'string' || typeof row.inserted !== 'string' ||
|
|
186
|
+
row.pos < 0 || row.pos + row.removed.length > length || !verify(pieces.length * 2 + 1)) return null;
|
|
187
|
+
const left = split(row.pos), right = split(row.pos + row.removed.length);
|
|
188
|
+
let at = 0;
|
|
189
|
+
for (let i = left; i < right; i++) {
|
|
190
|
+
const [start, end, text = before] = pieces[i];
|
|
191
|
+
if (!row.removed.startsWith(text.slice(start, end), at)) return null;
|
|
192
|
+
at += end - start;
|
|
193
|
+
}
|
|
194
|
+
if (row.removed === row.inserted) continue;
|
|
195
|
+
pieces.splice(left, right - left, [0, row.inserted.length, row.inserted]);
|
|
196
|
+
length += row.inserted.length - row.removed.length;
|
|
197
|
+
}
|
|
198
|
+
if (length !== after.length) return null;
|
|
199
|
+
const spans = [];
|
|
200
|
+
let a = 0, b = 0, at = 0;
|
|
201
|
+
for (const [start, end, text] of pieces) {
|
|
202
|
+
const size = end - start;
|
|
203
|
+
if (!verify(1) || !after.startsWith((text ?? before).slice(start, end), at)) return null;
|
|
204
|
+
if (text === undefined) {
|
|
205
|
+
let x = start, y = at, tail = end;
|
|
206
|
+
while (x < tail && (!safe(before, x) || !safe(after, y))) { x++; y++; if (!verify(1)) return null; }
|
|
207
|
+
while (tail > x && (!safe(before, tail) || !safe(after, at + tail - start))) { tail--; if (!verify(1)) return null; }
|
|
208
|
+
if (tail > x) {
|
|
209
|
+
if (a !== x || b !== y) spans.push([a, x, b, y]);
|
|
210
|
+
a = tail; b = at + tail - start;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
at += size;
|
|
214
|
+
}
|
|
215
|
+
if (a !== before.length || b !== after.length) spans.push([a, before.length, b, after.length]);
|
|
216
|
+
return spans;
|
|
217
|
+
}
|
|
218
|
+
return {record, name, walk, operations};
|
|
219
|
+
}
|
|
@@ -73,8 +73,8 @@ export function htmlImageRecord(raw) {
|
|
|
73
73
|
tokenEnd: raw.length, source: raw, placement: fence.layout, standalone: true, imageOnly: true};
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// The editor's bounded worker splits before layout annotation. Join only
|
|
77
|
-
// exact adjacent marker proven by this same source grammar, preserving offsets.
|
|
76
|
+
// The first phase of the editor's bounded worker splits before layout annotation. Join only
|
|
77
|
+
// the exact adjacent marker proven by this same source grammar, preserving offsets.
|
|
78
78
|
export function mergeHtmlObjectBlocks(blocks, source) {
|
|
79
79
|
for (let index = 0; index + 1 < blocks.length; index++) {
|
|
80
80
|
const block = blocks[index], next = blocks[index + 1];
|
|
@@ -104,7 +104,7 @@ export function htmlLayoutEdit(record, value) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// Change only owned header tokens. Body, fence spelling, class/id and unrelated attributes stay exact.
|
|
107
|
-
|
|
107
|
+
function htmlInfoEdit(info, changes) {
|
|
108
108
|
let text = info;
|
|
109
109
|
for (const [key, value] of Object.entries(changes)) {
|
|
110
110
|
const pattern = new RegExp('(^|[\\s{])\\s*' + key + '=(?:"[^"]*"|\'[^\']*\'|[^\\s}]+)', 'g');
|
package/dist/spec/md-layout.mjs
CHANGED
|
@@ -167,7 +167,7 @@ export function wrapTextBlock(element) {
|
|
|
167
167
|
element.textContent.trim() && !element.querySelector('img,table,pre,figure,math,.math-rendered') ? element : null;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
// The one wrap-owner rule (layout/browser.js,
|
|
170
|
+
// The one wrap-owner rule (layout/browser.js, kit/render.mjs, editor/share.js): text after, else before, skipping pictures,
|
|
171
171
|
// metadata and empty paragraphs; any other block is a barrier in that direction.
|
|
172
172
|
export function wrapNeighbour(node, classify) {
|
|
173
173
|
for (const step of [n => n && n.nextElementSibling, n => n && n.previousElementSibling]) {
|
package/dist/spec/md-marks.mjs
CHANGED
|
@@ -74,7 +74,7 @@ const TEXT_COLOR_HEX_TO_NAME = Object.freeze(
|
|
|
74
74
|
);
|
|
75
75
|
const PAGE_BREAK_MARKER = '<!--md-break:v1 page-->';
|
|
76
76
|
|
|
77
|
-
export { TEXT_COLOR_NAMES,
|
|
77
|
+
export { TEXT_COLOR_NAMES, PAGE_BREAK_MARKER };
|
|
78
78
|
|
|
79
79
|
export function formatColorRun(hex, content) {
|
|
80
80
|
return typography.wrap({color: hex}, content);
|
package/dist/spec/md-source.mjs
CHANGED
|
@@ -28,6 +28,10 @@ export function markdownSourcePositions(source, parser) {
|
|
|
28
28
|
}
|
|
29
29
|
return matched;
|
|
30
30
|
});
|
|
31
|
+
// A core rule may rewrite an inline token's content after its children were parsed (a task item drops
|
|
32
|
+
// its `[ ] `). Map rows from the text the inline rules read.
|
|
33
|
+
const read = new WeakMap();
|
|
34
|
+
inline.parse = function (text, md, env, children) { read.set(children, text); return parser.inline.parse.call(this, text, md, env, children); };
|
|
31
35
|
const {tokens, cells} = markdownTableSources(body, owner, {}, lines);
|
|
32
36
|
const readInline = (children, content, rawOffset) => {
|
|
33
37
|
for (const child of children || []) {
|
|
@@ -68,26 +72,30 @@ export function markdownSourcePositions(source, parser) {
|
|
|
68
72
|
}
|
|
69
73
|
continue;
|
|
70
74
|
}
|
|
71
|
-
|
|
75
|
+
const content = read.get(token.children) ?? token.content;
|
|
76
|
+
if (token.type !== 'inline' || !content) continue;
|
|
72
77
|
let rawOffset;
|
|
73
78
|
if (token.map) {
|
|
74
|
-
const offsets = [], starts = [], rows =
|
|
79
|
+
const offsets = [], starts = [], spaces = [], rows = content.split('\n');
|
|
75
80
|
let local = 0;
|
|
76
81
|
for (let i = 0; i < rows.length; i++) {
|
|
77
|
-
const start = lineAt(token.map[0] + i), end = lineAt(token.map[0] + i + 1);
|
|
78
|
-
|
|
82
|
+
const start = lineAt(token.map[0] + i), end = lineAt(token.map[0] + i + 1), line = source.slice(start, end).replace(/\0/g, '\ufffd');
|
|
83
|
+
let at = line.indexOf(rows[i]), lead = 0;
|
|
84
|
+
// A container that consumes part of a tab leaves the rest as spaces the source does not hold.
|
|
85
|
+
// They all stand for that tab.
|
|
86
|
+
if (at < 0) { const rest = rows[i].replace(/^ +/, ''); lead = rows[i].length - rest.length; at = line.indexOf(rest); if (!lead || line[at - 1] !== '\t') at = -1; }
|
|
79
87
|
if (at < 0) throw new Error('Markdown inline source mapping failed');
|
|
80
|
-
starts.push(local); offsets.push(start + at); local += rows[i].length + 1;
|
|
88
|
+
starts.push(local); spaces.push(lead); offsets.push(start + at - lead); local += rows[i].length + 1;
|
|
81
89
|
}
|
|
82
90
|
rawOffset = pos => {
|
|
83
91
|
let row = starts.length - 1;
|
|
84
92
|
while (row > 0 && starts[row] > pos) row--;
|
|
85
|
-
return offsets[row] + pos - starts[row];
|
|
93
|
+
return offsets[row] + Math.max(pos - starts[row], spaces[row] - 1);
|
|
86
94
|
};
|
|
87
95
|
} else if (cells.has(token)) {
|
|
88
96
|
rawOffset = cells.get(token);
|
|
89
97
|
} else continue;
|
|
90
|
-
readInline(token.children,
|
|
98
|
+
readInline(token.children, content, rawOffset);
|
|
91
99
|
}
|
|
92
100
|
literal.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
93
101
|
const merged = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-markdown-kit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "Read, render and write Self-contained Markdown: one .md file that carries its pictures, drawings and layout, and that any Markdown app can read. HTML renderer, stylesheet, text layout, Will/1 agent permissions, and an edit history with authorship and offline merge. MIT, no dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|