@adeu/core 1.14.0 → 1.15.1
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/index.cjs +2198 -1788
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -12
- package/dist/index.d.ts +7 -12
- package/dist/index.js +2198 -1788
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/comment_dedup.test.ts +269 -0
- package/src/domain.test.ts +280 -77
- package/src/domain.ts +146 -46
- package/src/engine.qa.test.ts +162 -0
- package/src/engine.ts +274 -89
- package/src/ingest.ts +283 -133
- package/src/mapper.ts +425 -196
- package/src/models.ts +2 -0
- package/src/repro_heading_bug.test.ts +174 -0
- package/src/search_write_engine.test.ts +112 -0
package/src/mapper.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DocumentObject } from
|
|
2
|
-
import { Paragraph, Table, Run, DocxEvent } from
|
|
3
|
-
import { findAllDescendants, findChild } from
|
|
4
|
-
import { extract_comments_data } from
|
|
1
|
+
import { DocumentObject } from "./docx/bridge.js";
|
|
2
|
+
import { Paragraph, Table, Run, DocxEvent } from "./docx/primitives.js";
|
|
3
|
+
import { findAllDescendants, findChild } from "./docx/dom.js";
|
|
4
|
+
import { extract_comments_data } from "./comments.js";
|
|
5
5
|
import {
|
|
6
6
|
_get_style_cache,
|
|
7
7
|
get_paragraph_prefix,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
iter_block_items,
|
|
13
13
|
iter_document_parts,
|
|
14
14
|
iter_paragraph_content,
|
|
15
|
-
} from
|
|
15
|
+
} from "./utils/docx.js";
|
|
16
16
|
|
|
17
17
|
export interface TextSpan {
|
|
18
18
|
start: number;
|
|
@@ -23,68 +23,81 @@ export interface TextSpan {
|
|
|
23
23
|
ins_id?: string | null;
|
|
24
24
|
del_id?: string | null;
|
|
25
25
|
hyperlink_id?: string | null;
|
|
26
|
+
comment_ids?: string[];
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export function renumber_snapshot_ids(
|
|
29
|
+
export function renumber_snapshot_ids(
|
|
30
|
+
doc: DocumentObject,
|
|
31
|
+
): [Record<string, string>, Record<string, string>] {
|
|
29
32
|
const chg_remap: Record<string, string> = {};
|
|
30
33
|
let next_chg = 1;
|
|
31
34
|
const body_root = doc.element;
|
|
32
35
|
|
|
33
36
|
const chg_elements: Element[] = [];
|
|
34
|
-
const all_elements = findAllDescendants(body_root,
|
|
37
|
+
const all_elements = findAllDescendants(body_root, "*");
|
|
35
38
|
for (const el of all_elements) {
|
|
36
|
-
if (el.tagName ===
|
|
39
|
+
if (el.tagName === "w:ins" || el.tagName === "w:del") {
|
|
37
40
|
chg_elements.push(el);
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
for (const elem of chg_elements) {
|
|
42
|
-
const old_id = elem.getAttribute(
|
|
45
|
+
const old_id = elem.getAttribute("w:id");
|
|
43
46
|
if (!old_id) continue;
|
|
44
47
|
if (chg_remap[old_id]) {
|
|
45
|
-
elem.setAttribute(
|
|
48
|
+
elem.setAttribute("w:id", chg_remap[old_id]);
|
|
46
49
|
continue;
|
|
47
50
|
}
|
|
48
51
|
const new_id = next_chg.toString();
|
|
49
52
|
chg_remap[old_id] = new_id;
|
|
50
|
-
elem.setAttribute(
|
|
53
|
+
elem.setAttribute("w:id", new_id);
|
|
51
54
|
next_chg++;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
const com_remap: Record<string, string> = {};
|
|
55
58
|
let next_com = 1;
|
|
56
|
-
const comments_part = doc.pkg.parts.find(
|
|
59
|
+
const comments_part = doc.pkg.parts.find(
|
|
60
|
+
(p) =>
|
|
61
|
+
p.contentType ===
|
|
62
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml",
|
|
63
|
+
);
|
|
57
64
|
|
|
58
65
|
if (comments_part) {
|
|
59
66
|
const comments_root = comments_part._element;
|
|
60
|
-
for (const c of findAllDescendants(comments_root,
|
|
61
|
-
const old_id = c.getAttribute(
|
|
67
|
+
for (const c of findAllDescendants(comments_root, "w:comment")) {
|
|
68
|
+
const old_id = c.getAttribute("w:id");
|
|
62
69
|
if (!old_id) continue;
|
|
63
70
|
if (com_remap[old_id]) {
|
|
64
|
-
c.setAttribute(
|
|
71
|
+
c.setAttribute("w:id", com_remap[old_id]);
|
|
65
72
|
continue;
|
|
66
73
|
}
|
|
67
74
|
const new_id = next_com.toString();
|
|
68
75
|
com_remap[old_id] = new_id;
|
|
69
|
-
c.setAttribute(
|
|
76
|
+
c.setAttribute("w:id", new_id);
|
|
70
77
|
next_com++;
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
for (const elem of all_elements) {
|
|
75
|
-
if (
|
|
76
|
-
|
|
82
|
+
if (
|
|
83
|
+
[
|
|
84
|
+
"w:commentReference",
|
|
85
|
+
"w:commentRangeStart",
|
|
86
|
+
"w:commentRangeEnd",
|
|
87
|
+
].includes(elem.tagName)
|
|
88
|
+
) {
|
|
89
|
+
const old_id = elem.getAttribute("w:id");
|
|
77
90
|
if (old_id && com_remap[old_id]) {
|
|
78
|
-
elem.setAttribute(
|
|
91
|
+
elem.setAttribute("w:id", com_remap[old_id]);
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
|
|
83
96
|
if (comments_part) {
|
|
84
|
-
for (const c of findAllDescendants(comments_part._element,
|
|
85
|
-
const parent_id = c.getAttribute(
|
|
97
|
+
for (const c of findAllDescendants(comments_part._element, "w:comment")) {
|
|
98
|
+
const parent_id = c.getAttribute("w15:p");
|
|
86
99
|
if (parent_id && com_remap[parent_id]) {
|
|
87
|
-
c.setAttribute(
|
|
100
|
+
c.setAttribute("w15:p", com_remap[parent_id]);
|
|
88
101
|
}
|
|
89
102
|
}
|
|
90
103
|
}
|
|
@@ -97,12 +110,16 @@ export class DocumentMapper {
|
|
|
97
110
|
public clean_view: boolean;
|
|
98
111
|
public original_view: boolean;
|
|
99
112
|
public comments_map: Record<string, any>;
|
|
100
|
-
public full_text: string =
|
|
113
|
+
public full_text: string = "";
|
|
101
114
|
public spans: TextSpan[] = [];
|
|
102
115
|
public appendix_start_index: number = -1;
|
|
103
116
|
private _text_chunks: string[] = [];
|
|
104
117
|
|
|
105
|
-
constructor(
|
|
118
|
+
constructor(
|
|
119
|
+
doc: DocumentObject,
|
|
120
|
+
clean_view: boolean = false,
|
|
121
|
+
original_view: boolean = false,
|
|
122
|
+
) {
|
|
106
123
|
this.doc = doc;
|
|
107
124
|
this.clean_view = clean_view;
|
|
108
125
|
this.original_view = original_view;
|
|
@@ -114,23 +131,29 @@ export class DocumentMapper {
|
|
|
114
131
|
let current_offset = 0;
|
|
115
132
|
this.spans = [];
|
|
116
133
|
this._text_chunks = [];
|
|
117
|
-
this.full_text =
|
|
134
|
+
this.full_text = "";
|
|
118
135
|
|
|
119
136
|
for (const part of iter_document_parts(this.doc)) {
|
|
120
137
|
current_offset = this._map_blocks(part, current_offset);
|
|
121
138
|
|
|
122
|
-
if (
|
|
123
|
-
this.
|
|
139
|
+
if (
|
|
140
|
+
this.spans.length > 0 &&
|
|
141
|
+
this.spans[this.spans.length - 1].text !== "\n\n"
|
|
142
|
+
) {
|
|
143
|
+
this._add_virtual_text("\n\n", current_offset, null);
|
|
124
144
|
current_offset += 2;
|
|
125
145
|
}
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
while (
|
|
148
|
+
while (
|
|
149
|
+
this.spans.length > 0 &&
|
|
150
|
+
this.spans[this.spans.length - 1].text === "\n\n"
|
|
151
|
+
) {
|
|
129
152
|
this.spans.pop();
|
|
130
153
|
this._text_chunks.pop();
|
|
131
154
|
}
|
|
132
155
|
|
|
133
|
-
this.full_text = this._text_chunks.join(
|
|
156
|
+
this.full_text = this._text_chunks.join("");
|
|
134
157
|
this.appendix_start_index = -1;
|
|
135
158
|
}
|
|
136
159
|
|
|
@@ -140,12 +163,13 @@ export class DocumentMapper {
|
|
|
140
163
|
const part = container.part || container;
|
|
141
164
|
const [style_cache, default_pstyle] = _get_style_cache(part);
|
|
142
165
|
|
|
143
|
-
if (c_type ===
|
|
144
|
-
const header =
|
|
166
|
+
if (c_type === "NotesPart") {
|
|
167
|
+
const header =
|
|
168
|
+
container.note_type === "fn" ? "## Footnotes" : "## Endnotes";
|
|
145
169
|
const sep = `---\n${header}`;
|
|
146
170
|
this._add_virtual_text(sep, current, null);
|
|
147
171
|
current += sep.length;
|
|
148
|
-
this._add_virtual_text(
|
|
172
|
+
this._add_virtual_text("\n\n", current, null);
|
|
149
173
|
current += 2;
|
|
150
174
|
}
|
|
151
175
|
|
|
@@ -155,17 +179,18 @@ export class DocumentMapper {
|
|
|
155
179
|
for (const item of iter_block_items(container)) {
|
|
156
180
|
const i_type = item.constructor.name;
|
|
157
181
|
|
|
158
|
-
if (i_type ===
|
|
182
|
+
if (i_type === "FootnoteItem") {
|
|
159
183
|
current = this._map_blocks(item, current);
|
|
160
184
|
} else if (item instanceof Paragraph) {
|
|
161
185
|
if (!is_first_para) {
|
|
162
|
-
const prev_para =
|
|
163
|
-
|
|
186
|
+
const prev_para =
|
|
187
|
+
previous_item instanceof Paragraph ? previous_item : null;
|
|
188
|
+
this._add_virtual_text("\n\n", current, prev_para);
|
|
164
189
|
current += 2;
|
|
165
190
|
}
|
|
166
191
|
|
|
167
192
|
let prefix = get_paragraph_prefix(item, style_cache, default_pstyle);
|
|
168
|
-
if (is_first_para && c_type ===
|
|
193
|
+
if (is_first_para && c_type === "FootnoteItem") {
|
|
169
194
|
prefix = `[^${container.note_type}-${container.id}]: ` + prefix;
|
|
170
195
|
}
|
|
171
196
|
if (prefix) {
|
|
@@ -173,13 +198,19 @@ export class DocumentMapper {
|
|
|
173
198
|
current += prefix.length;
|
|
174
199
|
}
|
|
175
200
|
|
|
176
|
-
current = this._map_paragraph_content(
|
|
201
|
+
current = this._map_paragraph_content(
|
|
202
|
+
item,
|
|
203
|
+
current,
|
|
204
|
+
style_cache,
|
|
205
|
+
default_pstyle,
|
|
206
|
+
);
|
|
177
207
|
is_first_para = false;
|
|
178
208
|
previous_item = item;
|
|
179
209
|
} else if (item instanceof Table) {
|
|
180
210
|
if (!is_first_para) {
|
|
181
|
-
const prev_para =
|
|
182
|
-
|
|
211
|
+
const prev_para =
|
|
212
|
+
previous_item instanceof Paragraph ? previous_item : null;
|
|
213
|
+
this._add_virtual_text("\n\n", current, prev_para);
|
|
183
214
|
current += 2;
|
|
184
215
|
}
|
|
185
216
|
current = this._map_table(item, current);
|
|
@@ -197,23 +228,23 @@ export class DocumentMapper {
|
|
|
197
228
|
|
|
198
229
|
for (const row of table.rows) {
|
|
199
230
|
const tr = row._element;
|
|
200
|
-
const trPr = findChild(tr,
|
|
201
|
-
const ins = trPr ? findChild(trPr,
|
|
202
|
-
const del_node = trPr ? findChild(trPr,
|
|
231
|
+
const trPr = findChild(tr, "w:trPr");
|
|
232
|
+
const ins = trPr ? findChild(trPr, "w:ins") : null;
|
|
233
|
+
const del_node = trPr ? findChild(trPr, "w:del") : null;
|
|
203
234
|
|
|
204
235
|
if (this.clean_view && del_node) continue;
|
|
205
236
|
if (this.original_view && ins) continue;
|
|
206
237
|
|
|
207
238
|
if (rows_processed > 0) {
|
|
208
|
-
this._add_virtual_text(
|
|
239
|
+
this._add_virtual_text("\n", current, null);
|
|
209
240
|
current += 1;
|
|
210
241
|
}
|
|
211
242
|
|
|
212
243
|
if (ins && !this.clean_view && !this.original_view) {
|
|
213
|
-
this._add_virtual_text(
|
|
244
|
+
this._add_virtual_text("{++ ", current, null);
|
|
214
245
|
current += 4;
|
|
215
246
|
} else if (del_node && !this.clean_view && !this.original_view) {
|
|
216
|
-
this._add_virtual_text(
|
|
247
|
+
this._add_virtual_text("{-- ", current, null);
|
|
217
248
|
current += 4;
|
|
218
249
|
}
|
|
219
250
|
|
|
@@ -225,7 +256,7 @@ export class DocumentMapper {
|
|
|
225
256
|
seen_cells.add(cell);
|
|
226
257
|
|
|
227
258
|
if (cells_processed > 0) {
|
|
228
|
-
this._add_virtual_text(
|
|
259
|
+
this._add_virtual_text(" | ", current, null);
|
|
229
260
|
current += 3;
|
|
230
261
|
}
|
|
231
262
|
|
|
@@ -234,11 +265,11 @@ export class DocumentMapper {
|
|
|
234
265
|
}
|
|
235
266
|
|
|
236
267
|
if (ins && !this.clean_view && !this.original_view) {
|
|
237
|
-
const suffix = ` |Chg:${ins.getAttribute(
|
|
268
|
+
const suffix = ` |Chg:${ins.getAttribute("w:id")}++}`;
|
|
238
269
|
this._add_virtual_text(suffix, current, null);
|
|
239
270
|
current += suffix.length;
|
|
240
271
|
} else if (del_node && !this.clean_view && !this.original_view) {
|
|
241
|
-
const suffix = ` |Chg:${del_node.getAttribute(
|
|
272
|
+
const suffix = ` |Chg:${del_node.getAttribute("w:id")}--}`;
|
|
242
273
|
this._add_virtual_text(suffix, current, null);
|
|
243
274
|
current += suffix.length;
|
|
244
275
|
}
|
|
@@ -251,11 +282,11 @@ export class DocumentMapper {
|
|
|
251
282
|
|
|
252
283
|
private _strip_markdown_formatting(text: string): string {
|
|
253
284
|
let result = text;
|
|
254
|
-
result = result.replace(/^#+\s*/gm,
|
|
255
|
-
result = result.replace(/\*\*(\w[\w\s]*\w|\w{2,})\*\*/g,
|
|
256
|
-
result = result.replace(/__(\w[\w\s]*\w|\w{2,})__/g,
|
|
257
|
-
result = result.replace(/(?<!\w)_(\w[\w\s]*\w|\w{2,})_(?!\w)/g,
|
|
258
|
-
result = result.replace(/(?<!\w)\*(\w[\w\s]*\w|\w{2,})\*(?!\w)/g,
|
|
285
|
+
result = result.replace(/^#+\s*/gm, "");
|
|
286
|
+
result = result.replace(/\*\*(\w[\w\s]*\w|\w{2,})\*\*/g, "$1");
|
|
287
|
+
result = result.replace(/__(\w[\w\s]*\w|\w{2,})__/g, "$1");
|
|
288
|
+
result = result.replace(/(?<!\w)_(\w[\w\s]*\w|\w{2,})_(?!\w)/g, "$1");
|
|
289
|
+
result = result.replace(/(?<!\w)\*(\w[\w\s]*\w|\w{2,})\*(?!\w)/g, "$1");
|
|
259
290
|
return result;
|
|
260
291
|
}
|
|
261
292
|
|
|
@@ -263,11 +294,17 @@ export class DocumentMapper {
|
|
|
263
294
|
paragraph: Paragraph,
|
|
264
295
|
start_offset: number,
|
|
265
296
|
style_cache?: any,
|
|
266
|
-
default_pstyle?: string | null
|
|
297
|
+
default_pstyle?: string | null,
|
|
267
298
|
): number {
|
|
268
299
|
let current = start_offset;
|
|
269
300
|
|
|
270
|
-
const span: TextSpan = {
|
|
301
|
+
const span: TextSpan = {
|
|
302
|
+
start: current,
|
|
303
|
+
end: current,
|
|
304
|
+
text: "",
|
|
305
|
+
run: null,
|
|
306
|
+
paragraph,
|
|
307
|
+
};
|
|
271
308
|
this.spans.push(span);
|
|
272
309
|
|
|
273
310
|
const active_ids = new Set<string>();
|
|
@@ -276,10 +313,17 @@ export class DocumentMapper {
|
|
|
276
313
|
const active_fmt: Record<string, DocxEvent> = {};
|
|
277
314
|
|
|
278
315
|
let deferred_meta_states: any[] = [];
|
|
279
|
-
let current_wrappers: [string, string] = [
|
|
280
|
-
let current_style: [string, string] = [
|
|
316
|
+
let current_wrappers: [string, string] = ["", ""];
|
|
317
|
+
let current_style: [string, string] = ["", ""];
|
|
281
318
|
let active_hyperlink_id: string | null = null;
|
|
282
|
-
let pending_runs: [
|
|
319
|
+
let pending_runs: [
|
|
320
|
+
string,
|
|
321
|
+
string,
|
|
322
|
+
Run | null,
|
|
323
|
+
string | null,
|
|
324
|
+
string | null,
|
|
325
|
+
string[],
|
|
326
|
+
][] = [];
|
|
283
327
|
|
|
284
328
|
const flush_pending_runs = () => {
|
|
285
329
|
if (pending_runs.length === 0) return;
|
|
@@ -288,14 +332,20 @@ export class DocumentMapper {
|
|
|
288
332
|
this._add_virtual_text(s_tok, current, paragraph);
|
|
289
333
|
current += s_tok.length;
|
|
290
334
|
}
|
|
291
|
-
for (const [kind, txt, r_obj, i_id, d_id] of pending_runs) {
|
|
292
|
-
if (kind ===
|
|
335
|
+
for (const [kind, txt, r_obj, i_id, d_id, c_ids] of pending_runs) {
|
|
336
|
+
if (kind === "virtual") {
|
|
293
337
|
this._add_virtual_text(txt, current, paragraph, active_hyperlink_id);
|
|
294
338
|
} else {
|
|
295
339
|
const s: TextSpan = {
|
|
296
|
-
start: current,
|
|
297
|
-
|
|
298
|
-
|
|
340
|
+
start: current,
|
|
341
|
+
end: current + txt.length,
|
|
342
|
+
text: txt,
|
|
343
|
+
run: r_obj,
|
|
344
|
+
paragraph,
|
|
345
|
+
ins_id: i_id || undefined,
|
|
346
|
+
del_id: d_id || undefined,
|
|
347
|
+
hyperlink_id: active_hyperlink_id || undefined,
|
|
348
|
+
comment_ids: c_ids.length > 0 ? c_ids : undefined,
|
|
299
349
|
};
|
|
300
350
|
this.spans.push(s);
|
|
301
351
|
this._text_chunks.push(txt);
|
|
@@ -310,8 +360,16 @@ export class DocumentMapper {
|
|
|
310
360
|
};
|
|
311
361
|
|
|
312
362
|
const items = Array.from(iter_paragraph_content(paragraph));
|
|
313
|
-
const is_heading = is_heading_paragraph(
|
|
314
|
-
|
|
363
|
+
const is_heading = is_heading_paragraph(
|
|
364
|
+
paragraph,
|
|
365
|
+
style_cache,
|
|
366
|
+
default_pstyle,
|
|
367
|
+
);
|
|
368
|
+
const native_heading = is_native_heading(
|
|
369
|
+
paragraph,
|
|
370
|
+
style_cache,
|
|
371
|
+
default_pstyle,
|
|
372
|
+
);
|
|
315
373
|
let leading_strip_active = is_heading;
|
|
316
374
|
|
|
317
375
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -323,24 +381,24 @@ export class DocumentMapper {
|
|
|
323
381
|
const text = get_run_text(item);
|
|
324
382
|
|
|
325
383
|
if (leading_strip_active) {
|
|
326
|
-
if (text ===
|
|
384
|
+
if (text === "" || /^\s*$/.test(text)) continue;
|
|
327
385
|
leading_strip_active = false;
|
|
328
386
|
}
|
|
329
387
|
|
|
330
|
-
if (text.includes(
|
|
331
|
-
const parts = text.split(
|
|
388
|
+
if (text.includes("\n") && (prefix || suffix)) {
|
|
389
|
+
const parts = text.split("\n");
|
|
332
390
|
for (let idx = 0; idx < parts.length; idx++) {
|
|
333
|
-
if (idx > 0) run_parts.push([
|
|
391
|
+
if (idx > 0) run_parts.push(["real", "\n", item]);
|
|
334
392
|
if (parts[idx]) {
|
|
335
|
-
if (prefix) run_parts.push([
|
|
336
|
-
run_parts.push([
|
|
337
|
-
if (suffix) run_parts.push([
|
|
393
|
+
if (prefix) run_parts.push(["virtual", prefix, null]);
|
|
394
|
+
run_parts.push(["real", parts[idx], item]);
|
|
395
|
+
if (suffix) run_parts.push(["virtual", suffix, null]);
|
|
338
396
|
}
|
|
339
397
|
}
|
|
340
398
|
} else {
|
|
341
|
-
if (prefix) run_parts.push([
|
|
342
|
-
if (text) run_parts.push([
|
|
343
|
-
if (suffix) run_parts.push([
|
|
399
|
+
if (prefix) run_parts.push(["virtual", prefix, null]);
|
|
400
|
+
if (text) run_parts.push(["real", text, item]);
|
|
401
|
+
if (suffix) run_parts.push(["virtual", suffix, null]);
|
|
344
402
|
}
|
|
345
403
|
|
|
346
404
|
if (this.clean_view && Object.keys(active_del).length > 0) {
|
|
@@ -350,88 +408,154 @@ export class DocumentMapper {
|
|
|
350
408
|
// pass
|
|
351
409
|
}
|
|
352
410
|
|
|
353
|
-
const full_seg_text = run_parts.map(x => x[1]).join(
|
|
411
|
+
const full_seg_text = run_parts.map((x) => x[1]).join("");
|
|
354
412
|
const curr_ins_id = Object.keys(active_ins).pop() || null;
|
|
355
413
|
const curr_del_id = Object.keys(active_del).pop() || null;
|
|
356
414
|
|
|
357
|
-
if (
|
|
358
|
-
|
|
415
|
+
if (
|
|
416
|
+
full_seg_text &&
|
|
417
|
+
!(this.clean_view && curr_del_id) &&
|
|
418
|
+
!(this.original_view && curr_ins_id)
|
|
419
|
+
) {
|
|
420
|
+
const new_wrappers =
|
|
421
|
+
this.clean_view || this.original_view
|
|
422
|
+
? (["", ""] as [string, string])
|
|
423
|
+
: this._get_wrappers(
|
|
424
|
+
curr_ins_id,
|
|
425
|
+
curr_del_id,
|
|
426
|
+
active_ids,
|
|
427
|
+
active_fmt,
|
|
428
|
+
);
|
|
359
429
|
const new_style: [string, string] = [prefix, suffix];
|
|
360
430
|
|
|
361
|
-
if (
|
|
431
|
+
if (
|
|
432
|
+
pending_runs.length > 0 &&
|
|
433
|
+
new_wrappers[0] === current_wrappers[0] &&
|
|
434
|
+
new_wrappers[1] === current_wrappers[1]
|
|
435
|
+
) {
|
|
362
436
|
let skip_leading_prefix = false;
|
|
363
|
-
if (
|
|
364
|
-
|
|
365
|
-
|
|
437
|
+
if (
|
|
438
|
+
new_style[0] === current_style[0] &&
|
|
439
|
+
new_style[1] === current_style[1] &&
|
|
440
|
+
current_style[0] !== "" &&
|
|
441
|
+
pending_runs[pending_runs.length - 1][0] === "virtual" &&
|
|
442
|
+
pending_runs[pending_runs.length - 1][1] === current_style[1]
|
|
443
|
+
) {
|
|
366
444
|
pending_runs.pop();
|
|
367
445
|
skip_leading_prefix = true;
|
|
368
446
|
}
|
|
369
447
|
|
|
448
|
+
const curr_comment_ids = Array.from(active_ids);
|
|
370
449
|
for (const [kind, txt, r_obj] of run_parts) {
|
|
371
|
-
if (
|
|
450
|
+
if (
|
|
451
|
+
skip_leading_prefix &&
|
|
452
|
+
kind === "virtual" &&
|
|
453
|
+
txt === new_style[0]
|
|
454
|
+
) {
|
|
372
455
|
skip_leading_prefix = false;
|
|
373
456
|
continue;
|
|
374
457
|
}
|
|
375
|
-
pending_runs.push([
|
|
458
|
+
pending_runs.push([
|
|
459
|
+
kind,
|
|
460
|
+
txt,
|
|
461
|
+
r_obj,
|
|
462
|
+
curr_ins_id,
|
|
463
|
+
curr_del_id,
|
|
464
|
+
curr_comment_ids,
|
|
465
|
+
]);
|
|
376
466
|
}
|
|
377
467
|
current_style = new_style;
|
|
378
468
|
} else {
|
|
379
469
|
flush_pending_runs();
|
|
380
470
|
current_wrappers = new_wrappers;
|
|
381
471
|
current_style = new_style;
|
|
472
|
+
const curr_comment_ids = Array.from(active_ids);
|
|
382
473
|
for (const [kind, txt, r_obj] of run_parts) {
|
|
383
|
-
pending_runs.push([
|
|
474
|
+
pending_runs.push([
|
|
475
|
+
kind,
|
|
476
|
+
txt,
|
|
477
|
+
r_obj,
|
|
478
|
+
curr_ins_id,
|
|
479
|
+
curr_del_id,
|
|
480
|
+
curr_comment_ids,
|
|
481
|
+
]);
|
|
384
482
|
}
|
|
385
483
|
}
|
|
386
484
|
}
|
|
387
485
|
|
|
388
486
|
if (!this.clean_view && !this.original_view) {
|
|
389
|
-
const has_meta =
|
|
487
|
+
const has_meta =
|
|
488
|
+
Object.keys(active_ins).length > 0 ||
|
|
489
|
+
Object.keys(active_del).length > 0 ||
|
|
490
|
+
active_ids.size > 0 ||
|
|
491
|
+
Object.keys(active_fmt).length > 0;
|
|
390
492
|
if (has_meta) {
|
|
391
|
-
deferred_meta_states.push([
|
|
493
|
+
deferred_meta_states.push([
|
|
494
|
+
{ ...active_ins },
|
|
495
|
+
{ ...active_del },
|
|
496
|
+
new Set(active_ids),
|
|
497
|
+
{ ...active_fmt },
|
|
498
|
+
]);
|
|
392
499
|
}
|
|
393
500
|
|
|
394
501
|
let should_defer = false;
|
|
395
|
-
const
|
|
502
|
+
const has_any_meta =
|
|
503
|
+
curr_ins_id !== null ||
|
|
504
|
+
curr_del_id !== null ||
|
|
505
|
+
Object.keys(active_fmt).length > 0 ||
|
|
506
|
+
active_ids.size > 0;
|
|
396
507
|
|
|
397
|
-
if (
|
|
508
|
+
if (has_any_meta) {
|
|
398
509
|
let j = i + 1;
|
|
399
|
-
let
|
|
510
|
+
let next_has_meta = false;
|
|
400
511
|
let temp_ins_count = Object.keys(active_ins).length;
|
|
401
512
|
let temp_del_count = Object.keys(active_del).length;
|
|
402
513
|
let temp_fmt_count = Object.keys(active_fmt).length;
|
|
514
|
+
const temp_comment_ids = new Set(active_ids);
|
|
403
515
|
|
|
404
516
|
while (j < items.length) {
|
|
405
517
|
const next_item = items[j];
|
|
406
518
|
if (next_item instanceof Run) {
|
|
407
519
|
if (!get_run_text(next_item)) {
|
|
408
|
-
j++;
|
|
520
|
+
j++;
|
|
521
|
+
continue;
|
|
409
522
|
}
|
|
410
|
-
if (
|
|
411
|
-
|
|
523
|
+
if (
|
|
524
|
+
temp_ins_count > 0 ||
|
|
525
|
+
temp_del_count > 0 ||
|
|
526
|
+
temp_fmt_count > 0 ||
|
|
527
|
+
temp_comment_ids.size > 0
|
|
528
|
+
) {
|
|
529
|
+
next_has_meta = true;
|
|
412
530
|
}
|
|
413
531
|
break;
|
|
414
532
|
} else {
|
|
415
533
|
const ev = next_item as DocxEvent;
|
|
416
|
-
if (ev.type ===
|
|
417
|
-
else if (ev.type ===
|
|
418
|
-
|
|
419
|
-
else if (ev.type ===
|
|
420
|
-
else if (ev.type ===
|
|
421
|
-
|
|
534
|
+
if (ev.type === "ins_start") temp_ins_count++;
|
|
535
|
+
else if (ev.type === "ins_end")
|
|
536
|
+
temp_ins_count = Math.max(0, temp_ins_count - 1);
|
|
537
|
+
else if (ev.type === "del_start") temp_del_count++;
|
|
538
|
+
else if (ev.type === "del_end")
|
|
539
|
+
temp_del_count = Math.max(0, temp_del_count - 1);
|
|
540
|
+
else if (ev.type === "fmt_start") temp_fmt_count++;
|
|
541
|
+
else if (ev.type === "fmt_end")
|
|
542
|
+
temp_fmt_count = Math.max(0, temp_fmt_count - 1);
|
|
543
|
+
else if (ev.type === "start") temp_comment_ids.add(ev.id);
|
|
544
|
+
else if (ev.type === "end") temp_comment_ids.delete(ev.id);
|
|
422
545
|
}
|
|
423
546
|
j++;
|
|
424
547
|
}
|
|
425
548
|
|
|
426
|
-
if (
|
|
549
|
+
if (next_has_meta) should_defer = true;
|
|
427
550
|
}
|
|
428
551
|
|
|
429
552
|
if (!should_defer && deferred_meta_states.length > 0) {
|
|
430
|
-
const meta_block =
|
|
553
|
+
const meta_block =
|
|
554
|
+
this._build_merged_meta_block(deferred_meta_states);
|
|
431
555
|
if (meta_block) {
|
|
432
556
|
flush_pending_runs();
|
|
433
|
-
current_wrappers = [
|
|
434
|
-
current_style = [
|
|
557
|
+
current_wrappers = ["", ""];
|
|
558
|
+
current_style = ["", ""];
|
|
435
559
|
const full_meta = `{>>${meta_block}<<}`;
|
|
436
560
|
this._add_virtual_text(full_meta, current, paragraph);
|
|
437
561
|
current += full_meta.length;
|
|
@@ -443,57 +567,57 @@ export class DocumentMapper {
|
|
|
443
567
|
const ev = item as DocxEvent;
|
|
444
568
|
leading_strip_active = false;
|
|
445
569
|
flush_pending_runs();
|
|
446
|
-
current_wrappers = [
|
|
447
|
-
current_style = [
|
|
448
|
-
|
|
449
|
-
if (ev.type ===
|
|
450
|
-
else if (ev.type ===
|
|
451
|
-
else if (ev.type ===
|
|
452
|
-
else if (ev.type ===
|
|
453
|
-
else if (ev.type ===
|
|
454
|
-
else if (ev.type ===
|
|
455
|
-
else if (ev.type ===
|
|
456
|
-
else if (ev.type ===
|
|
457
|
-
else if (ev.type ===
|
|
570
|
+
current_wrappers = ["", ""];
|
|
571
|
+
current_style = ["", ""];
|
|
572
|
+
|
|
573
|
+
if (ev.type === "start") active_ids.add(ev.id);
|
|
574
|
+
else if (ev.type === "end") active_ids.delete(ev.id);
|
|
575
|
+
else if (ev.type === "ins_start") active_ins[ev.id] = ev;
|
|
576
|
+
else if (ev.type === "ins_end") delete active_ins[ev.id];
|
|
577
|
+
else if (ev.type === "del_start") active_del[ev.id] = ev;
|
|
578
|
+
else if (ev.type === "del_end") delete active_del[ev.id];
|
|
579
|
+
else if (ev.type === "fmt_start") active_fmt[ev.id] = ev;
|
|
580
|
+
else if (ev.type === "fmt_end") delete active_fmt[ev.id];
|
|
581
|
+
else if (ev.type === "footnote" || ev.type === "endnote") {
|
|
458
582
|
flush_pending_runs();
|
|
459
|
-
current_wrappers = [
|
|
460
|
-
current_style = [
|
|
461
|
-
const prefix_str = ev.type ===
|
|
583
|
+
current_wrappers = ["", ""];
|
|
584
|
+
current_style = ["", ""];
|
|
585
|
+
const prefix_str = ev.type === "footnote" ? "fn" : "en";
|
|
462
586
|
const txt = `[^${prefix_str}-${ev.id}]`;
|
|
463
587
|
this._add_virtual_text(txt, current, paragraph);
|
|
464
588
|
current += txt.length;
|
|
465
|
-
} else if (ev.type ===
|
|
589
|
+
} else if (ev.type === "hyperlink_start") {
|
|
466
590
|
flush_pending_runs();
|
|
467
|
-
current_wrappers = [
|
|
468
|
-
current_style = [
|
|
469
|
-
this._add_virtual_text(
|
|
591
|
+
current_wrappers = ["", ""];
|
|
592
|
+
current_style = ["", ""];
|
|
593
|
+
this._add_virtual_text("[", current, paragraph, ev.id);
|
|
470
594
|
current += 1;
|
|
471
595
|
active_hyperlink_id = ev.id;
|
|
472
|
-
} else if (ev.type ===
|
|
596
|
+
} else if (ev.type === "hyperlink_end") {
|
|
473
597
|
flush_pending_runs();
|
|
474
|
-
current_wrappers = [
|
|
475
|
-
current_style = [
|
|
598
|
+
current_wrappers = ["", ""];
|
|
599
|
+
current_style = ["", ""];
|
|
476
600
|
const txt = `](${ev.date})`;
|
|
477
601
|
this._add_virtual_text(txt, current, paragraph, ev.id);
|
|
478
602
|
current += txt.length;
|
|
479
603
|
active_hyperlink_id = null;
|
|
480
|
-
} else if (ev.type ===
|
|
604
|
+
} else if (ev.type === "xref_start") {
|
|
481
605
|
flush_pending_runs();
|
|
482
|
-
current_wrappers = [
|
|
483
|
-
current_style = [
|
|
484
|
-
this._add_virtual_text(
|
|
606
|
+
current_wrappers = ["", ""];
|
|
607
|
+
current_style = ["", ""];
|
|
608
|
+
this._add_virtual_text("[~", current, paragraph);
|
|
485
609
|
current += 2;
|
|
486
|
-
} else if (ev.type ===
|
|
610
|
+
} else if (ev.type === "xref_end") {
|
|
487
611
|
flush_pending_runs();
|
|
488
|
-
current_wrappers = [
|
|
489
|
-
current_style = [
|
|
612
|
+
current_wrappers = ["", ""];
|
|
613
|
+
current_style = ["", ""];
|
|
490
614
|
const txt = `~](#${ev.id})`;
|
|
491
615
|
this._add_virtual_text(txt, current, paragraph);
|
|
492
616
|
current += txt.length;
|
|
493
|
-
} else if (ev.type ===
|
|
617
|
+
} else if (ev.type === "bookmark") {
|
|
494
618
|
flush_pending_runs();
|
|
495
|
-
current_wrappers = [
|
|
496
|
-
current_style = [
|
|
619
|
+
current_wrappers = ["", ""];
|
|
620
|
+
current_style = ["", ""];
|
|
497
621
|
const txt = `{#${ev.id}}`;
|
|
498
622
|
this._add_virtual_text(txt, current, paragraph);
|
|
499
623
|
current += txt.length;
|
|
@@ -515,11 +639,17 @@ export class DocumentMapper {
|
|
|
515
639
|
return current;
|
|
516
640
|
}
|
|
517
641
|
|
|
518
|
-
private _get_wrappers(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
642
|
+
private _get_wrappers(
|
|
643
|
+
ins_id: string | null,
|
|
644
|
+
del_id: string | null,
|
|
645
|
+
active_ids: Set<string>,
|
|
646
|
+
active_fmt: Record<string, DocxEvent>,
|
|
647
|
+
): [string, string] {
|
|
648
|
+
if (del_id) return ["{--", "--}"];
|
|
649
|
+
if (ins_id) return ["{++", "++}"];
|
|
650
|
+
if (active_ids.size > 0 || Object.keys(active_fmt).length > 0)
|
|
651
|
+
return ["{==", "==}"];
|
|
652
|
+
return ["", ""];
|
|
523
653
|
}
|
|
524
654
|
|
|
525
655
|
private _build_merged_meta_block(states_list: any[]): string {
|
|
@@ -528,26 +658,32 @@ export class DocumentMapper {
|
|
|
528
658
|
const seen_sigs = new Set<string>();
|
|
529
659
|
|
|
530
660
|
for (const [ins_map, del_map, comments_set, fmt_map] of states_list) {
|
|
531
|
-
for (const [uid, meta] of Object.entries(
|
|
661
|
+
for (const [uid, meta] of Object.entries(
|
|
662
|
+
ins_map as Record<string, DocxEvent>,
|
|
663
|
+
)) {
|
|
532
664
|
const sig = `Chg:${uid}`;
|
|
533
665
|
if (!seen_sigs.has(sig)) {
|
|
534
|
-
const auth = meta.author ||
|
|
666
|
+
const auth = meta.author || "Unknown";
|
|
535
667
|
change_lines.push(`[${sig} insert] ${auth}`);
|
|
536
668
|
seen_sigs.add(sig);
|
|
537
669
|
}
|
|
538
670
|
}
|
|
539
|
-
for (const [uid, meta] of Object.entries(
|
|
671
|
+
for (const [uid, meta] of Object.entries(
|
|
672
|
+
del_map as Record<string, DocxEvent>,
|
|
673
|
+
)) {
|
|
540
674
|
const sig = `Chg:${uid}`;
|
|
541
675
|
if (!seen_sigs.has(sig)) {
|
|
542
|
-
const auth = meta.author ||
|
|
676
|
+
const auth = meta.author || "Unknown";
|
|
543
677
|
change_lines.push(`[${sig} delete] ${auth}`);
|
|
544
678
|
seen_sigs.add(sig);
|
|
545
679
|
}
|
|
546
680
|
}
|
|
547
|
-
for (const [uid, meta] of Object.entries(
|
|
681
|
+
for (const [uid, meta] of Object.entries(
|
|
682
|
+
fmt_map as Record<string, DocxEvent>,
|
|
683
|
+
)) {
|
|
548
684
|
const sig = `Chg:${uid}`;
|
|
549
685
|
if (!seen_sigs.has(sig)) {
|
|
550
|
-
const auth = meta.author ||
|
|
686
|
+
const auth = meta.author || "Unknown";
|
|
551
687
|
change_lines.push(`[${sig} format] ${auth}`);
|
|
552
688
|
seen_sigs.add(sig);
|
|
553
689
|
}
|
|
@@ -568,24 +704,33 @@ export class DocumentMapper {
|
|
|
568
704
|
}
|
|
569
705
|
}
|
|
570
706
|
|
|
571
|
-
return [...change_lines, ...comment_lines].join(
|
|
707
|
+
return [...change_lines, ...comment_lines].join("\n");
|
|
572
708
|
}
|
|
573
709
|
|
|
574
|
-
private _add_virtual_text(
|
|
710
|
+
private _add_virtual_text(
|
|
711
|
+
text: string,
|
|
712
|
+
offset: number,
|
|
713
|
+
context_paragraph: Paragraph | null,
|
|
714
|
+
hyperlink_id: string | null = null,
|
|
715
|
+
) {
|
|
575
716
|
const span: TextSpan = {
|
|
576
717
|
start: offset,
|
|
577
718
|
end: offset + text.length,
|
|
578
719
|
text,
|
|
579
720
|
run: null,
|
|
580
721
|
paragraph: context_paragraph,
|
|
581
|
-
hyperlink_id: hyperlink_id || undefined
|
|
722
|
+
hyperlink_id: hyperlink_id || undefined,
|
|
582
723
|
};
|
|
583
724
|
this.spans.push(span);
|
|
584
725
|
this._text_chunks.push(text);
|
|
585
726
|
}
|
|
586
727
|
|
|
587
728
|
private _replace_smart_quotes(text: string): string {
|
|
588
|
-
return text
|
|
729
|
+
return text
|
|
730
|
+
.replace(/“/g, '"')
|
|
731
|
+
.replace(/”/g, '"')
|
|
732
|
+
.replace(/‘/g, "'")
|
|
733
|
+
.replace(/’/g, "'");
|
|
589
734
|
}
|
|
590
735
|
|
|
591
736
|
private _make_fuzzy_regex(target_text: string): string {
|
|
@@ -594,10 +739,11 @@ export class DocumentMapper {
|
|
|
594
739
|
|
|
595
740
|
const parts: string[] = [];
|
|
596
741
|
const token_pattern = /(\[_+\])|(\s+)|(['"])|([.,;:\/])/g;
|
|
597
|
-
|
|
742
|
+
|
|
598
743
|
let last_idx = 0;
|
|
599
744
|
let match;
|
|
600
|
-
const escapeRegExp = (str: string) =>
|
|
745
|
+
const escapeRegExp = (str: string) =>
|
|
746
|
+
str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
601
747
|
|
|
602
748
|
while ((match = token_pattern.exec(target_text)) !== null) {
|
|
603
749
|
const literal = target_text.substring(last_idx, match.index);
|
|
@@ -609,18 +755,18 @@ export class DocumentMapper {
|
|
|
609
755
|
const g_punct = match[4];
|
|
610
756
|
|
|
611
757
|
if (g_placeholder) {
|
|
612
|
-
parts.push(
|
|
758
|
+
parts.push("\\[_+\\]");
|
|
613
759
|
} else if (g_space) {
|
|
614
|
-
parts.push(
|
|
615
|
-
parts.push(
|
|
616
|
-
parts.push(
|
|
760
|
+
parts.push("(?:\\*\\*|__|\\*|_)?");
|
|
761
|
+
parts.push("\\s+");
|
|
762
|
+
parts.push("(?:\\*\\*|__|\\*|_)?");
|
|
617
763
|
} else if (g_quote) {
|
|
618
|
-
if (g_quote === "'") parts.push(
|
|
764
|
+
if (g_quote === "'") parts.push("[\u2018\u2019']");
|
|
619
765
|
else parts.push('["\u201c\u201d]');
|
|
620
766
|
} else if (g_punct) {
|
|
621
|
-
parts.push(
|
|
767
|
+
parts.push("(?:\\*\\*|__|\\*|_)?");
|
|
622
768
|
parts.push(escapeRegExp(g_punct));
|
|
623
|
-
parts.push(
|
|
769
|
+
parts.push("(?:\\*\\*|__|\\*|_)?");
|
|
624
770
|
}
|
|
625
771
|
|
|
626
772
|
last_idx = token_pattern.lastIndex;
|
|
@@ -629,10 +775,22 @@ export class DocumentMapper {
|
|
|
629
775
|
const remaining = target_text.substring(last_idx);
|
|
630
776
|
if (remaining) parts.push(escapeRegExp(remaining));
|
|
631
777
|
|
|
632
|
-
return parts.join(
|
|
778
|
+
return parts.join("");
|
|
633
779
|
}
|
|
634
780
|
|
|
635
|
-
public find_match_index(
|
|
781
|
+
public find_match_index(
|
|
782
|
+
target_text: string,
|
|
783
|
+
is_regex: boolean = false,
|
|
784
|
+
): [number, number] {
|
|
785
|
+
if (is_regex) {
|
|
786
|
+
try {
|
|
787
|
+
const pattern = new RegExp(target_text);
|
|
788
|
+
const match = pattern.exec(this.full_text);
|
|
789
|
+
if (match) return [match.index, match[0].length];
|
|
790
|
+
} catch (e) {}
|
|
791
|
+
return [-1, 0];
|
|
792
|
+
}
|
|
793
|
+
|
|
636
794
|
let start_idx = this.full_text.indexOf(target_text);
|
|
637
795
|
if (start_idx !== -1) return [start_idx, target_text.length];
|
|
638
796
|
|
|
@@ -656,26 +814,49 @@ export class DocumentMapper {
|
|
|
656
814
|
return [-1, 0];
|
|
657
815
|
}
|
|
658
816
|
|
|
659
|
-
public find_all_match_indices(
|
|
817
|
+
public find_all_match_indices(
|
|
818
|
+
target_text: string,
|
|
819
|
+
is_regex: boolean = false,
|
|
820
|
+
): [number, number][] {
|
|
660
821
|
if (!target_text) return [];
|
|
661
|
-
const escapeRegExp = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
662
822
|
|
|
663
|
-
|
|
664
|
-
|
|
823
|
+
if (is_regex) {
|
|
824
|
+
try {
|
|
825
|
+
const pattern = new RegExp(target_text, "g");
|
|
826
|
+
const matches = [...this.full_text.matchAll(pattern)];
|
|
827
|
+
if (matches.length > 0)
|
|
828
|
+
return matches.map((m) => [m.index!, m[0].length]);
|
|
829
|
+
} catch (e) {}
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
const escapeRegExp = (str: string) =>
|
|
833
|
+
str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
834
|
+
|
|
835
|
+
let matches = [
|
|
836
|
+
...this.full_text.matchAll(new RegExp(escapeRegExp(target_text), "g")),
|
|
837
|
+
];
|
|
838
|
+
if (matches.length > 0) return matches.map((m) => [m.index!, m[0].length]);
|
|
665
839
|
|
|
666
840
|
const norm_full = this._replace_smart_quotes(this.full_text);
|
|
667
841
|
const norm_target = this._replace_smart_quotes(target_text);
|
|
668
|
-
matches = [
|
|
669
|
-
|
|
842
|
+
matches = [
|
|
843
|
+
...norm_full.matchAll(new RegExp(escapeRegExp(norm_target), "g")),
|
|
844
|
+
];
|
|
845
|
+
if (matches.length > 0) return matches.map((m) => [m.index!, m[0].length]);
|
|
670
846
|
|
|
671
847
|
const stripped_target = this._strip_markdown_formatting(target_text);
|
|
672
|
-
matches = [
|
|
673
|
-
|
|
848
|
+
matches = [
|
|
849
|
+
...this.full_text.matchAll(
|
|
850
|
+
new RegExp(escapeRegExp(stripped_target), "g"),
|
|
851
|
+
),
|
|
852
|
+
];
|
|
853
|
+
if (matches.length > 0) return matches.map((m) => [m.index!, m[0].length]);
|
|
674
854
|
|
|
675
855
|
try {
|
|
676
|
-
const pattern = new RegExp(this._make_fuzzy_regex(target_text),
|
|
856
|
+
const pattern = new RegExp(this._make_fuzzy_regex(target_text), "g");
|
|
677
857
|
matches = [...this.full_text.matchAll(pattern)];
|
|
678
|
-
if (matches.length > 0)
|
|
858
|
+
if (matches.length > 0)
|
|
859
|
+
return matches.map((m) => [m.index!, m[0].length]);
|
|
679
860
|
} catch (e) {}
|
|
680
861
|
|
|
681
862
|
return [];
|
|
@@ -687,39 +868,69 @@ export class DocumentMapper {
|
|
|
687
868
|
return this._resolve_runs_at_range(start_idx, start_idx + length);
|
|
688
869
|
}
|
|
689
870
|
|
|
690
|
-
public find_target_runs_by_index(
|
|
691
|
-
|
|
871
|
+
public find_target_runs_by_index(
|
|
872
|
+
start_index: number,
|
|
873
|
+
length: number,
|
|
874
|
+
rebuild_map = true,
|
|
875
|
+
): Run[] {
|
|
876
|
+
return this._resolve_runs_at_range(
|
|
877
|
+
start_index,
|
|
878
|
+
start_index + length,
|
|
879
|
+
rebuild_map,
|
|
880
|
+
);
|
|
692
881
|
}
|
|
693
882
|
|
|
694
|
-
public get_virtual_spans_in_range(
|
|
883
|
+
public get_virtual_spans_in_range(
|
|
884
|
+
start_index: number,
|
|
885
|
+
length: number,
|
|
886
|
+
): TextSpan[] {
|
|
695
887
|
const end_index = start_index + length;
|
|
696
|
-
return this.spans.filter(
|
|
888
|
+
return this.spans.filter(
|
|
889
|
+
(s) =>
|
|
890
|
+
s.run === null &&
|
|
891
|
+
s.text === "\n\n" &&
|
|
892
|
+
s.start >= start_index &&
|
|
893
|
+
s.end <= end_index,
|
|
894
|
+
);
|
|
697
895
|
}
|
|
698
896
|
|
|
699
|
-
private _resolve_runs_at_range(
|
|
700
|
-
|
|
897
|
+
private _resolve_runs_at_range(
|
|
898
|
+
start_idx: number,
|
|
899
|
+
end_idx: number,
|
|
900
|
+
rebuild_map = true,
|
|
901
|
+
): Run[] {
|
|
902
|
+
const affected_spans = this.spans.filter(
|
|
903
|
+
(s) => s.end > start_idx && s.start < end_idx,
|
|
904
|
+
);
|
|
701
905
|
if (affected_spans.length === 0) return [];
|
|
702
906
|
|
|
703
|
-
const working_runs = affected_spans
|
|
907
|
+
const working_runs = affected_spans
|
|
908
|
+
.filter((s) => s.run !== null)
|
|
909
|
+
.map((s) => s.run!);
|
|
704
910
|
if (working_runs.length === 0) return [];
|
|
705
911
|
|
|
706
912
|
let dom_modified = false;
|
|
707
913
|
|
|
708
|
-
const first_real_span = affected_spans.find(s => s.run !== null);
|
|
914
|
+
const first_real_span = affected_spans.find((s) => s.run !== null);
|
|
709
915
|
let start_split_adjustment = 0;
|
|
710
916
|
|
|
711
917
|
if (first_real_span) {
|
|
712
918
|
const local_start = start_idx - first_real_span.start;
|
|
713
919
|
if (local_start > 0) {
|
|
714
920
|
const idx_in_working = 0;
|
|
715
|
-
const [, right_run] = this._split_run_at_index(
|
|
921
|
+
const [, right_run] = this._split_run_at_index(
|
|
922
|
+
working_runs[idx_in_working],
|
|
923
|
+
local_start,
|
|
924
|
+
);
|
|
716
925
|
working_runs[idx_in_working] = right_run;
|
|
717
926
|
dom_modified = true;
|
|
718
927
|
start_split_adjustment = local_start;
|
|
719
928
|
}
|
|
720
929
|
}
|
|
721
930
|
|
|
722
|
-
const last_real_span = [...affected_spans]
|
|
931
|
+
const last_real_span = [...affected_spans]
|
|
932
|
+
.reverse()
|
|
933
|
+
.find((s) => s.run !== null);
|
|
723
934
|
|
|
724
935
|
if (last_real_span) {
|
|
725
936
|
const is_same_run = first_real_span === last_real_span;
|
|
@@ -746,8 +957,11 @@ export class DocumentMapper {
|
|
|
746
957
|
return working_runs;
|
|
747
958
|
}
|
|
748
959
|
|
|
749
|
-
public get_insertion_anchor(
|
|
750
|
-
|
|
960
|
+
public get_insertion_anchor(
|
|
961
|
+
index: number,
|
|
962
|
+
rebuild_map = true,
|
|
963
|
+
): [Run | null, Paragraph | null] {
|
|
964
|
+
const preceding = this.spans.filter((s) => s.end === index);
|
|
751
965
|
if (preceding.length > 0) {
|
|
752
966
|
for (let i = preceding.length - 1; i >= 0; i--) {
|
|
753
967
|
if (preceding[i].run) return [preceding[i].run, preceding[i].paragraph];
|
|
@@ -757,7 +971,9 @@ export class DocumentMapper {
|
|
|
757
971
|
}
|
|
758
972
|
}
|
|
759
973
|
|
|
760
|
-
const containing = this.spans.filter(
|
|
974
|
+
const containing = this.spans.filter(
|
|
975
|
+
(s) => s.start < index && index < s.end,
|
|
976
|
+
);
|
|
761
977
|
if (containing.length > 0) {
|
|
762
978
|
const span = containing[0];
|
|
763
979
|
if (span.run === null) {
|
|
@@ -767,7 +983,7 @@ export class DocumentMapper {
|
|
|
767
983
|
return [null, span.paragraph];
|
|
768
984
|
} else {
|
|
769
985
|
const offset = index - span.start;
|
|
770
|
-
const [left
|
|
986
|
+
const [left] = this._split_run_at_index(span.run, offset);
|
|
771
987
|
if (rebuild_map) this._build_map();
|
|
772
988
|
return [left, span.paragraph];
|
|
773
989
|
}
|
|
@@ -779,16 +995,18 @@ export class DocumentMapper {
|
|
|
779
995
|
return [null, null];
|
|
780
996
|
}
|
|
781
997
|
|
|
782
|
-
const preceding_gap = this.spans.filter(s => s.end < index);
|
|
998
|
+
const preceding_gap = this.spans.filter((s) => s.end < index);
|
|
783
999
|
if (preceding_gap.length > 0) {
|
|
784
1000
|
for (let i = preceding_gap.length - 1; i >= 0; i--) {
|
|
785
|
-
if (preceding_gap[i].run)
|
|
1001
|
+
if (preceding_gap[i].run)
|
|
1002
|
+
return [preceding_gap[i].run, preceding_gap[i].paragraph];
|
|
786
1003
|
}
|
|
787
1004
|
for (let i = preceding_gap.length - 1; i >= 0; i--) {
|
|
788
|
-
if (preceding_gap[i].paragraph)
|
|
1005
|
+
if (preceding_gap[i].paragraph)
|
|
1006
|
+
return [null, preceding_gap[i].paragraph];
|
|
789
1007
|
}
|
|
790
1008
|
}
|
|
791
|
-
|
|
1009
|
+
|
|
792
1010
|
return [null, null];
|
|
793
1011
|
}
|
|
794
1012
|
|
|
@@ -803,7 +1021,10 @@ export class DocumentMapper {
|
|
|
803
1021
|
this._set_run_text_elements(new_r_element, right_text);
|
|
804
1022
|
|
|
805
1023
|
if (run._element.parentNode) {
|
|
806
|
-
run._element.parentNode.insertBefore(
|
|
1024
|
+
run._element.parentNode.insertBefore(
|
|
1025
|
+
new_r_element,
|
|
1026
|
+
run._element.nextSibling,
|
|
1027
|
+
);
|
|
807
1028
|
}
|
|
808
1029
|
|
|
809
1030
|
const new_run = new Run(new_r_element, run._parent);
|
|
@@ -814,7 +1035,10 @@ export class DocumentMapper {
|
|
|
814
1035
|
const to_remove: Element[] = [];
|
|
815
1036
|
for (let i = 0; i < r_element.childNodes.length; i++) {
|
|
816
1037
|
const child = r_element.childNodes[i] as Element;
|
|
817
|
-
if (
|
|
1038
|
+
if (
|
|
1039
|
+
child.nodeType === 1 &&
|
|
1040
|
+
["w:t", "w:delText", "w:br", "w:cr", "w:tab"].includes(child.tagName)
|
|
1041
|
+
) {
|
|
818
1042
|
to_remove.push(child);
|
|
819
1043
|
}
|
|
820
1044
|
}
|
|
@@ -824,18 +1048,23 @@ export class DocumentMapper {
|
|
|
824
1048
|
|
|
825
1049
|
const doc = r_element.ownerDocument;
|
|
826
1050
|
if (doc) {
|
|
827
|
-
const new_t = doc.createElement(
|
|
1051
|
+
const new_t = doc.createElement("w:t");
|
|
828
1052
|
new_t.textContent = new_text;
|
|
829
1053
|
if (new_text.trim() !== new_text) {
|
|
830
|
-
new_t.setAttribute(
|
|
1054
|
+
new_t.setAttribute("xml:space", "preserve");
|
|
831
1055
|
}
|
|
832
1056
|
r_element.appendChild(new_t);
|
|
833
1057
|
}
|
|
834
1058
|
}
|
|
835
1059
|
|
|
836
|
-
public get_context_at_range(
|
|
837
|
-
|
|
1060
|
+
public get_context_at_range(
|
|
1061
|
+
start_idx: number,
|
|
1062
|
+
end_idx: number,
|
|
1063
|
+
): TextSpan | null {
|
|
1064
|
+
const real_spans = this.spans.filter(
|
|
1065
|
+
(s) => s.run && s.end > start_idx && s.start < end_idx,
|
|
1066
|
+
);
|
|
838
1067
|
if (real_spans.length > 0) return real_spans[0];
|
|
839
1068
|
return null;
|
|
840
1069
|
}
|
|
841
|
-
}
|
|
1070
|
+
}
|