@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/engine.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "./models.js";
|
|
14
14
|
import { trim_common_context } from "./diff.js";
|
|
15
15
|
import { findChild, findAllDescendants, serializeXml } from "./docx/dom.js";
|
|
16
|
+
import { split_structural_appendix, paginate } from "./pagination.js";
|
|
16
17
|
import {
|
|
17
18
|
is_heading_paragraph,
|
|
18
19
|
is_native_heading,
|
|
@@ -279,7 +280,8 @@ export class RedlineEngine {
|
|
|
279
280
|
start_idx + length + 30,
|
|
280
281
|
);
|
|
281
282
|
|
|
282
|
-
const
|
|
283
|
+
const insertion = new_text ? `{++${new_text}++}` : "";
|
|
284
|
+
const critic_markup = `${context_before}{--${target_text}--}${insertion}${context_after}`;
|
|
283
285
|
|
|
284
286
|
let clean_text = critic_markup;
|
|
285
287
|
clean_text = clean_text.replace(/\{>>.*?<<\}/gs, "");
|
|
@@ -301,6 +303,40 @@ export class RedlineEngine {
|
|
|
301
303
|
return maxId;
|
|
302
304
|
}
|
|
303
305
|
|
|
306
|
+
private _get_heading_path_and_page(start_idx: number, text: string, page_offsets: number[]): [string, number] {
|
|
307
|
+
let page = 1;
|
|
308
|
+
for (let i = 0; i < page_offsets.length; i++) {
|
|
309
|
+
if (start_idx >= page_offsets[i]) {
|
|
310
|
+
page = i + 1;
|
|
311
|
+
} else {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const textBefore = text.substring(0, start_idx);
|
|
317
|
+
const lines = textBefore.split("\n");
|
|
318
|
+
const path: string[] = [];
|
|
319
|
+
let current_level = 999;
|
|
320
|
+
|
|
321
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
322
|
+
const line = lines[i];
|
|
323
|
+
const m = line.match(/^(#{1,6})\s+(.*)/);
|
|
324
|
+
if (m) {
|
|
325
|
+
const level = m[1].length;
|
|
326
|
+
if (level < current_level) {
|
|
327
|
+
let cleanHeading = m[2].replace(/\*\*|__|[*_]/g, "").replace(/\{#[^}]+\}/g, "").trim();
|
|
328
|
+
if (cleanHeading.length > 80) {
|
|
329
|
+
cleanHeading = cleanHeading.substring(0, 80) + "...";
|
|
330
|
+
}
|
|
331
|
+
path.unshift(cleanHeading);
|
|
332
|
+
current_level = level;
|
|
333
|
+
if (level === 1) break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return [path.join(" > "), page];
|
|
338
|
+
}
|
|
339
|
+
|
|
304
340
|
public accept_all_revisions() {
|
|
305
341
|
const parts_to_process: Element[] = [this.doc.element];
|
|
306
342
|
|
|
@@ -1193,15 +1229,18 @@ export class RedlineEngine {
|
|
|
1193
1229
|
for (let i = 0; i < edits.length; i++) {
|
|
1194
1230
|
const edit = edits[i];
|
|
1195
1231
|
if (!edit.target_text) continue;
|
|
1232
|
+
|
|
1233
|
+
const is_regex = (edit as any).regex || false;
|
|
1234
|
+
const match_mode = (edit as any).match_mode || "strict";
|
|
1196
1235
|
|
|
1197
|
-
let matches = this.mapper.find_all_match_indices(edit.target_text);
|
|
1236
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
1198
1237
|
let activeText = this.mapper.full_text;
|
|
1199
1238
|
let target_mapper = this.mapper;
|
|
1200
1239
|
|
|
1201
1240
|
if (matches.length === 0) {
|
|
1202
1241
|
if (!this.clean_mapper)
|
|
1203
1242
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
1204
|
-
matches = this.clean_mapper.find_all_match_indices(edit.target_text);
|
|
1243
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
1205
1244
|
if (matches.length > 0) {
|
|
1206
1245
|
activeText = this.clean_mapper.full_text;
|
|
1207
1246
|
target_mapper = this.clean_mapper;
|
|
@@ -1233,7 +1272,7 @@ export class RedlineEngine {
|
|
|
1233
1272
|
if (!this.original_mapper) {
|
|
1234
1273
|
this.original_mapper = new DocumentMapper(this.doc, false, true);
|
|
1235
1274
|
}
|
|
1236
|
-
const orig_matches = this.original_mapper.find_all_match_indices(edit.target_text);
|
|
1275
|
+
const orig_matches = this.original_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
1237
1276
|
if (orig_matches.length > 0) {
|
|
1238
1277
|
is_deleted_text = true;
|
|
1239
1278
|
for (const [start, length] of orig_matches) {
|
|
@@ -1272,7 +1311,7 @@ export class RedlineEngine {
|
|
|
1272
1311
|
`- Edit ${i + 1} Failed: Target text not found in document:\n "${edit.target_text}"`,
|
|
1273
1312
|
);
|
|
1274
1313
|
}
|
|
1275
|
-
} else if (matches.length > 1) {
|
|
1314
|
+
} else if (matches.length > 1 && match_mode === "strict") {
|
|
1276
1315
|
const positions: [number, number][] = matches.map(([start, length]) => [
|
|
1277
1316
|
start,
|
|
1278
1317
|
start + length,
|
|
@@ -1343,6 +1382,14 @@ export class RedlineEngine {
|
|
|
1343
1382
|
if (auth && auth !== this.author) nestedAuthors.add(auth);
|
|
1344
1383
|
}
|
|
1345
1384
|
}
|
|
1385
|
+
if (s.comment_ids) {
|
|
1386
|
+
for (const cid of s.comment_ids) {
|
|
1387
|
+
const c_data = this.mapper.comments_map[cid];
|
|
1388
|
+
if (c_data && c_data.author && c_data.author !== this.author) {
|
|
1389
|
+
nestedAuthors.add(c_data.author);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1346
1393
|
}
|
|
1347
1394
|
if (nestedAuthors.size > 0) {
|
|
1348
1395
|
errors.push(
|
|
@@ -1501,6 +1548,10 @@ export class RedlineEngine {
|
|
|
1501
1548
|
}
|
|
1502
1549
|
}
|
|
1503
1550
|
|
|
1551
|
+
const [body_text] = split_structural_appendix(this.mapper.full_text);
|
|
1552
|
+
const pag_res = paginate(body_text, "");
|
|
1553
|
+
const page_offsets = pag_res.body_page_offsets;
|
|
1554
|
+
|
|
1504
1555
|
const edits_reports: any[] = [];
|
|
1505
1556
|
let applied_edits = 0;
|
|
1506
1557
|
let skipped_edits = 0;
|
|
@@ -1525,9 +1576,8 @@ export class RedlineEngine {
|
|
|
1525
1576
|
});
|
|
1526
1577
|
continue;
|
|
1527
1578
|
}
|
|
1528
|
-
const res = this.apply_edits([edit]);
|
|
1529
|
-
|
|
1530
|
-
if (applied > 0) {
|
|
1579
|
+
const res = this.apply_edits([edit], page_offsets);
|
|
1580
|
+
if ((edit as any)._applied_status) {
|
|
1531
1581
|
applied_edits++;
|
|
1532
1582
|
const previews = this._build_edit_context_previews(edit);
|
|
1533
1583
|
edits_reports.push({
|
|
@@ -1538,6 +1588,10 @@ export class RedlineEngine {
|
|
|
1538
1588
|
error: null,
|
|
1539
1589
|
critic_markup: previews[0],
|
|
1540
1590
|
clean_text: previews[1],
|
|
1591
|
+
pages: (edit as any)._pages || [],
|
|
1592
|
+
heading_path: (edit as any)._heading_path || "",
|
|
1593
|
+
occurrences_modified: (edit as any)._occurrences_modified || 0,
|
|
1594
|
+
match_mode: (edit as any).match_mode || "strict",
|
|
1541
1595
|
});
|
|
1542
1596
|
} else {
|
|
1543
1597
|
skipped_edits++;
|
|
@@ -1562,9 +1616,9 @@ export class RedlineEngine {
|
|
|
1562
1616
|
throw new BatchValidationError(errors);
|
|
1563
1617
|
}
|
|
1564
1618
|
const cloned_edits = edits.map((e) => JSON.parse(JSON.stringify(e)));
|
|
1565
|
-
const res = this.apply_edits(cloned_edits);
|
|
1566
|
-
applied_edits =
|
|
1567
|
-
skipped_edits =
|
|
1619
|
+
const res = this.apply_edits(cloned_edits, page_offsets);
|
|
1620
|
+
applied_edits = cloned_edits.filter((e) => (e as any)._applied_status).length;
|
|
1621
|
+
skipped_edits = cloned_edits.length - applied_edits;
|
|
1568
1622
|
|
|
1569
1623
|
for (const edit of cloned_edits) {
|
|
1570
1624
|
const success = (edit as any)._applied_status || false;
|
|
@@ -1587,6 +1641,10 @@ export class RedlineEngine {
|
|
|
1587
1641
|
error: error_msg,
|
|
1588
1642
|
critic_markup: critic_markup,
|
|
1589
1643
|
clean_text: clean_text,
|
|
1644
|
+
pages: (edit as any)._pages || [],
|
|
1645
|
+
heading_path: (edit as any)._heading_path || "",
|
|
1646
|
+
occurrences_modified: (edit as any)._occurrences_modified || 0,
|
|
1647
|
+
match_mode: (edit as any).match_mode || "strict",
|
|
1590
1648
|
});
|
|
1591
1649
|
}
|
|
1592
1650
|
}
|
|
@@ -1604,9 +1662,14 @@ export class RedlineEngine {
|
|
|
1604
1662
|
};
|
|
1605
1663
|
}
|
|
1606
1664
|
|
|
1607
|
-
public apply_edits(edits: any[]): [number, number] {
|
|
1665
|
+
public apply_edits(edits: any[], page_offsets: number[] = []): [number, number] {
|
|
1608
1666
|
let applied = 0;
|
|
1609
1667
|
let skipped = 0;
|
|
1668
|
+
|
|
1669
|
+
if (!page_offsets || page_offsets.length === 0) {
|
|
1670
|
+
const [body_text] = split_structural_appendix(this.mapper.full_text);
|
|
1671
|
+
page_offsets = paginate(body_text, "").body_page_offsets;
|
|
1672
|
+
}
|
|
1610
1673
|
const resolved_edits: [any, string | null][] = [];
|
|
1611
1674
|
|
|
1612
1675
|
for (const edit of edits) {
|
|
@@ -1728,6 +1791,19 @@ export class RedlineEngine {
|
|
|
1728
1791
|
const parent = edit._parent_edit_ref;
|
|
1729
1792
|
if (parent) {
|
|
1730
1793
|
parent._applied_status = true;
|
|
1794
|
+
parent._occurrences_modified = (parent._occurrences_modified || 0) + 1;
|
|
1795
|
+
const [path, page] = this._get_heading_path_and_page(start, this.mapper.full_text, page_offsets);
|
|
1796
|
+
const pages: number[] = parent._pages || [];
|
|
1797
|
+
if (!pages.includes(page)) pages.unshift(page);
|
|
1798
|
+
parent._pages = pages;
|
|
1799
|
+
parent._heading_path = path;
|
|
1800
|
+
} else {
|
|
1801
|
+
edit._occurrences_modified = (edit._occurrences_modified || 0) + 1;
|
|
1802
|
+
const [path, page] = this._get_heading_path_and_page(start, this.mapper.full_text, page_offsets);
|
|
1803
|
+
const pages: number[] = edit._pages || [];
|
|
1804
|
+
if (!pages.includes(page)) pages.unshift(page);
|
|
1805
|
+
edit._pages = pages;
|
|
1806
|
+
edit._heading_path = path;
|
|
1731
1807
|
}
|
|
1732
1808
|
} else {
|
|
1733
1809
|
skipped++;
|
|
@@ -1899,103 +1975,138 @@ export class RedlineEngine {
|
|
|
1899
1975
|
return false;
|
|
1900
1976
|
}
|
|
1901
1977
|
|
|
1902
|
-
/**
|
|
1903
|
-
* Returns the first match of `target_text` in the raw mapper that is NOT
|
|
1904
|
-
* entirely contained within a tracked deletion (<w:del>). Tracked-deleted
|
|
1905
|
-
* copies are not live, editable text, so an edit must resolve to a live
|
|
1906
|
-
* occurrence even when a dead copy appears earlier in the document
|
|
1907
|
-
* (BUG-23-5). Falls back to the plain first match when no live copy is
|
|
1908
|
-
* found (e.g. fuzzy/normalized matches the span filter cannot align).
|
|
1909
|
-
*/
|
|
1910
|
-
private _first_live_match(target_text: string): [number, number] {
|
|
1911
|
-
const all = this.mapper.find_all_match_indices(target_text);
|
|
1912
|
-
if (all.length <= 1) {
|
|
1913
|
-
return this.mapper.find_match_index(target_text);
|
|
1914
|
-
}
|
|
1915
|
-
for (const [start, length] of all) {
|
|
1916
|
-
const realSpans = this.mapper.spans.filter(
|
|
1917
|
-
(s) => s.run !== null && s.end > start && s.start < start + length,
|
|
1918
|
-
);
|
|
1919
|
-
if (realSpans.length === 0) return [start, length];
|
|
1920
|
-
if (realSpans.some((s) => !s.del_id)) return [start, length];
|
|
1921
|
-
}
|
|
1922
|
-
return this.mapper.find_match_index(target_text);
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
1978
|
private _pre_resolve_heuristic_edit(edit: any): any {
|
|
1926
1979
|
if (!edit.target_text) return null;
|
|
1927
1980
|
|
|
1928
|
-
|
|
1981
|
+
const is_regex = edit.regex || false;
|
|
1982
|
+
const match_mode = edit.match_mode || "strict";
|
|
1983
|
+
|
|
1984
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
1929
1985
|
let use_clean_map = false;
|
|
1930
1986
|
|
|
1931
|
-
if (
|
|
1987
|
+
if (matches.length === 0) {
|
|
1932
1988
|
if (!this.clean_mapper)
|
|
1933
1989
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
);
|
|
1937
|
-
if (start_idx !== -1) use_clean_map = true;
|
|
1990
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
1991
|
+
if (matches.length > 0) use_clean_map = true;
|
|
1938
1992
|
else return null;
|
|
1939
1993
|
}
|
|
1940
1994
|
|
|
1941
1995
|
const active_mapper = use_clean_map ? this.clean_mapper! : this.mapper;
|
|
1942
|
-
const effective_new_text = edit.new_text || "";
|
|
1943
|
-
const actual_doc_text = this.mapper.full_text.substring(
|
|
1944
|
-
start_idx,
|
|
1945
|
-
start_idx + match_len,
|
|
1946
|
-
);
|
|
1947
1996
|
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
comment: edit.comment,
|
|
1957
|
-
_match_start_index: start_idx,
|
|
1958
|
-
_internal_op: "COMMENT_ONLY",
|
|
1959
|
-
_active_mapper_ref: active_mapper,
|
|
1960
|
-
};
|
|
1997
|
+
let live_matches: [number, number][] = [];
|
|
1998
|
+
for (const [s, match_len] of matches) {
|
|
1999
|
+
const realSpans = active_mapper.spans.filter(
|
|
2000
|
+
(span) => span.run !== null && span.end > s && span.start < s + match_len
|
|
2001
|
+
);
|
|
2002
|
+
if (realSpans.length === 0 || realSpans.some((span) => !span.del_id)) {
|
|
2003
|
+
live_matches.push([s, match_len]);
|
|
2004
|
+
}
|
|
1961
2005
|
}
|
|
1962
2006
|
|
|
1963
|
-
|
|
1964
|
-
let final_target = "";
|
|
1965
|
-
let final_new = "";
|
|
1966
|
-
let effective_start_idx = start_idx;
|
|
2007
|
+
if (live_matches.length === 0) return null;
|
|
1967
2008
|
|
|
1968
|
-
if (
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2009
|
+
if (match_mode === "strict" || match_mode === "first") {
|
|
2010
|
+
live_matches = live_matches.slice(0, 1);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
const all_sub_edits: any[] = [];
|
|
2014
|
+
|
|
2015
|
+
for (const [start_idx, match_len] of live_matches) {
|
|
2016
|
+
const actual_doc_text = active_mapper.full_text.substring(
|
|
2017
|
+
start_idx,
|
|
2018
|
+
start_idx + match_len,
|
|
1976
2019
|
);
|
|
1977
|
-
|
|
1978
|
-
|
|
2020
|
+
let current_effective_new_text = edit.new_text || "";
|
|
2021
|
+
|
|
2022
|
+
if (is_regex && current_effective_new_text) {
|
|
2023
|
+
try {
|
|
2024
|
+
current_effective_new_text = actual_doc_text.replace(new RegExp(edit.target_text), current_effective_new_text);
|
|
2025
|
+
} catch (e) {}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(edit.target_text);
|
|
2029
|
+
const [edit_new_clean, edit_new_style] = this._parse_markdown_style(current_effective_new_text);
|
|
2030
|
+
|
|
2031
|
+
if (edit_target_style !== edit_new_style) {
|
|
2032
|
+
const [actual_clean] = this._parse_markdown_style(actual_doc_text);
|
|
2033
|
+
const final_target = actual_clean;
|
|
2034
|
+
const final_new = edit_new_clean;
|
|
2035
|
+
const style_op = final_target === final_new ? "STYLE_ONLY" : "STYLE_AND_TEXT";
|
|
2036
|
+
const prefix_offset = actual_doc_text.indexOf(actual_clean);
|
|
2037
|
+
const effective_start_idx = start_idx + (prefix_offset !== -1 ? prefix_offset : 0);
|
|
2038
|
+
const resolved_style = edit_new_style !== null ? edit_new_style : "Normal";
|
|
2039
|
+
|
|
2040
|
+
all_sub_edits.push({
|
|
2041
|
+
type: "modify",
|
|
2042
|
+
target_text: final_target,
|
|
2043
|
+
new_text: final_new,
|
|
2044
|
+
comment: edit.comment,
|
|
2045
|
+
_match_start_index: effective_start_idx,
|
|
2046
|
+
_internal_op: style_op,
|
|
2047
|
+
_new_style: resolved_style,
|
|
2048
|
+
_active_mapper_ref: active_mapper,
|
|
2049
|
+
});
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
1979
2052
|
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2053
|
+
if (
|
|
2054
|
+
actual_doc_text === current_effective_new_text ||
|
|
2055
|
+
edit.target_text === current_effective_new_text
|
|
2056
|
+
) {
|
|
2057
|
+
all_sub_edits.push({
|
|
2058
|
+
type: "modify",
|
|
2059
|
+
target_text: actual_doc_text,
|
|
2060
|
+
new_text: actual_doc_text,
|
|
2061
|
+
comment: edit.comment,
|
|
2062
|
+
_match_start_index: start_idx,
|
|
2063
|
+
_internal_op: "COMMENT_ONLY",
|
|
2064
|
+
_active_mapper_ref: active_mapper,
|
|
2065
|
+
});
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
let effective_op = "";
|
|
2070
|
+
let final_target = "";
|
|
2071
|
+
let final_new = "";
|
|
2072
|
+
let effective_start_idx = start_idx;
|
|
1983
2073
|
|
|
1984
|
-
if (
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2074
|
+
if (current_effective_new_text.startsWith(actual_doc_text)) {
|
|
2075
|
+
effective_op = "INSERTION";
|
|
2076
|
+
final_new = current_effective_new_text.substring(actual_doc_text.length);
|
|
2077
|
+
effective_start_idx = start_idx + match_len;
|
|
2078
|
+
} else {
|
|
2079
|
+
const [prefix_len, suffix_len] = trim_common_context(
|
|
2080
|
+
actual_doc_text,
|
|
2081
|
+
current_effective_new_text,
|
|
2082
|
+
);
|
|
2083
|
+
const t_end = actual_doc_text.length - suffix_len;
|
|
2084
|
+
const n_end = current_effective_new_text.length - suffix_len;
|
|
2085
|
+
|
|
2086
|
+
final_target = actual_doc_text.substring(prefix_len, t_end);
|
|
2087
|
+
final_new = current_effective_new_text.substring(prefix_len, n_end);
|
|
2088
|
+
effective_start_idx = start_idx + prefix_len;
|
|
2089
|
+
|
|
2090
|
+
if (!final_target && final_new) effective_op = "INSERTION";
|
|
2091
|
+
else if (final_target && !final_new) effective_op = "DELETION";
|
|
2092
|
+
else if (final_target && final_new) effective_op = "MODIFICATION";
|
|
2093
|
+
else effective_op = "COMMENT_ONLY";
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
all_sub_edits.push({
|
|
2097
|
+
type: "modify",
|
|
2098
|
+
target_text: final_target,
|
|
2099
|
+
new_text: final_new,
|
|
2100
|
+
comment: edit.comment,
|
|
2101
|
+
_match_start_index: effective_start_idx,
|
|
2102
|
+
_internal_op: effective_op,
|
|
2103
|
+
_active_mapper_ref: active_mapper,
|
|
2104
|
+
});
|
|
1988
2105
|
}
|
|
1989
2106
|
|
|
1990
|
-
return
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
new_text: final_new,
|
|
1994
|
-
comment: edit.comment,
|
|
1995
|
-
_match_start_index: effective_start_idx,
|
|
1996
|
-
_internal_op: effective_op,
|
|
1997
|
-
_active_mapper_ref: active_mapper,
|
|
1998
|
-
};
|
|
2107
|
+
if (all_sub_edits.length === 0) return null;
|
|
2108
|
+
if (match_mode === "all" || all_sub_edits.length > 1) return all_sub_edits;
|
|
2109
|
+
return all_sub_edits[0];
|
|
1999
2110
|
}
|
|
2000
2111
|
|
|
2001
2112
|
private _apply_single_edit_indexed(
|
|
@@ -2012,6 +2123,80 @@ export class RedlineEngine {
|
|
|
2012
2123
|
: edit._match_start_index || 0;
|
|
2013
2124
|
const length = edit.target_text ? edit.target_text.length : 0;
|
|
2014
2125
|
|
|
2126
|
+
if (op === "STYLE_ONLY" || op === "STYLE_AND_TEXT") {
|
|
2127
|
+
const [anchor_run, anchor_para] = active_mapper.get_insertion_anchor(
|
|
2128
|
+
start_idx,
|
|
2129
|
+
rebuild_map,
|
|
2130
|
+
);
|
|
2131
|
+
let target_para_el: Element | null = null;
|
|
2132
|
+
if (anchor_para) {
|
|
2133
|
+
target_para_el = anchor_para._element;
|
|
2134
|
+
} else if (anchor_run) {
|
|
2135
|
+
let walker: Element | null = anchor_run._element;
|
|
2136
|
+
while (walker && walker.tagName !== "w:p") {
|
|
2137
|
+
walker = walker.parentNode as Element | null;
|
|
2138
|
+
}
|
|
2139
|
+
target_para_el = walker;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
if (target_para_el && edit._new_style) {
|
|
2143
|
+
this._set_paragraph_style(target_para_el, edit._new_style);
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
if (op === "STYLE_ONLY") {
|
|
2147
|
+
if (edit.comment) {
|
|
2148
|
+
const target_runs = active_mapper.find_target_runs_by_index(
|
|
2149
|
+
start_idx,
|
|
2150
|
+
length,
|
|
2151
|
+
rebuild_map,
|
|
2152
|
+
);
|
|
2153
|
+
if (target_runs.length > 0) {
|
|
2154
|
+
const first_el = target_runs[0]._element;
|
|
2155
|
+
const last_el = target_runs[target_runs.length - 1]._element;
|
|
2156
|
+
let start_p: Element | null = first_el;
|
|
2157
|
+
while (start_p && start_p.tagName !== "w:p")
|
|
2158
|
+
start_p = start_p.parentNode as Element;
|
|
2159
|
+
let end_p: Element | null = last_el;
|
|
2160
|
+
while (end_p && end_p.tagName !== "w:p")
|
|
2161
|
+
end_p = end_p.parentNode as Element;
|
|
2162
|
+
if (start_p && end_p) {
|
|
2163
|
+
const ascend_to_paragraph_child = (el: Element, p: Element): Element => {
|
|
2164
|
+
let cur: Element = el;
|
|
2165
|
+
while (cur.parentNode && cur.parentNode !== p) {
|
|
2166
|
+
cur = cur.parentNode as Element;
|
|
2167
|
+
}
|
|
2168
|
+
return cur;
|
|
2169
|
+
};
|
|
2170
|
+
const first_anchor = ascend_to_paragraph_child(first_el, start_p);
|
|
2171
|
+
const last_anchor = ascend_to_paragraph_child(last_el, end_p);
|
|
2172
|
+
if (start_p === end_p) {
|
|
2173
|
+
this._attach_comment(start_p, first_anchor, last_anchor, edit.comment);
|
|
2174
|
+
} else {
|
|
2175
|
+
this._attach_comment_spanning(
|
|
2176
|
+
start_p,
|
|
2177
|
+
first_anchor,
|
|
2178
|
+
end_p,
|
|
2179
|
+
last_anchor,
|
|
2180
|
+
edit.comment,
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
return true;
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
if (edit.target_text && edit.new_text) {
|
|
2190
|
+
op = "MODIFICATION";
|
|
2191
|
+
} else if (!edit.target_text && edit.new_text) {
|
|
2192
|
+
op = "INSERTION";
|
|
2193
|
+
} else if (edit.target_text && !edit.new_text) {
|
|
2194
|
+
op = "DELETION";
|
|
2195
|
+
} else {
|
|
2196
|
+
op = "COMMENT_ONLY";
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2015
2200
|
const del_id = ["DELETION", "MODIFICATION"].includes(op)
|
|
2016
2201
|
? this._getNextId()
|
|
2017
2202
|
: null;
|