docxdriver 0.4.2__tar.gz → 0.4.4__tar.gz
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.
- {docxdriver-0.4.2 → docxdriver-0.4.4}/Cargo.lock +6 -6
- {docxdriver-0.4.2 → docxdriver-0.4.4}/Cargo.toml +1 -1
- {docxdriver-0.4.2 → docxdriver-0.4.4}/PKG-INFO +2 -1
- {docxdriver-0.4.2/crates/docxdriver-python → docxdriver-0.4.4}/README.md +1 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/api.rs +167 -4
- docxdriver-0.4.4/crates/docxdriver-core/src/commands/bookmarks.rs +879 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/mod.rs +23 -2
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/table_structure.rs +47 -2
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/typed.rs +79 -6
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/facade.rs +7 -1
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/segments.rs +5 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4/crates/docxdriver-python}/README.md +1 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4/crates/docxdriver-python}/python/docxdriver/document.py +35 -4
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-python/python/docxdriver/plan.py +60 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-python/src/lib.rs +6 -0
- docxdriver-0.4.4/crates/docxdriver-python/tests/test_document.py +126 -0
- {docxdriver-0.4.2/crates/docxdriver-python → docxdriver-0.4.4}/python/docxdriver/document.py +35 -4
- {docxdriver-0.4.2 → docxdriver-0.4.4}/python/docxdriver/plan.py +60 -0
- docxdriver-0.4.2/crates/docxdriver-python/tests/test_document.py +0 -75
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/Cargo.toml +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/LICENSE +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/README.md +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/assets/blank.docx +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/comments.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/equations.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/headers.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/layout.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/lists.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/reading.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/readonly.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/revisions.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/sections.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/commands/tables.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/document.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/assets.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/build.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/chrome.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/compact.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/ctx.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/input.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/mathml_omml.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/mod.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/omml_latex.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/omml_mathml.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/parse.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/render.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/styles.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/html/surface.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/integrity/mod.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/integrity/schema.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/integrity/tree.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/integrity/wml-model.json +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/lib.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/media.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/outcome.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-core/src/package.rs +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-python/.gitignore +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-python/Cargo.toml +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-python/python/docxdriver/__init__.py +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/crates/docxdriver-python/tests/test_plan.py +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/pyproject.toml +0 -0
- {docxdriver-0.4.2 → docxdriver-0.4.4}/python/docxdriver/__init__.py +0 -0
|
@@ -416,7 +416,7 @@ dependencies = [
|
|
|
416
416
|
|
|
417
417
|
[[package]]
|
|
418
418
|
name = "docxdriver-cli"
|
|
419
|
-
version = "0.4.
|
|
419
|
+
version = "0.4.4"
|
|
420
420
|
dependencies = [
|
|
421
421
|
"clap",
|
|
422
422
|
"docxdriver-core",
|
|
@@ -427,7 +427,7 @@ dependencies = [
|
|
|
427
427
|
|
|
428
428
|
[[package]]
|
|
429
429
|
name = "docxdriver-core"
|
|
430
|
-
version = "0.4.
|
|
430
|
+
version = "0.4.4"
|
|
431
431
|
dependencies = [
|
|
432
432
|
"hex",
|
|
433
433
|
"latex2mathml",
|
|
@@ -445,7 +445,7 @@ dependencies = [
|
|
|
445
445
|
|
|
446
446
|
[[package]]
|
|
447
447
|
name = "docxdriver-mcp"
|
|
448
|
-
version = "0.4.
|
|
448
|
+
version = "0.4.4"
|
|
449
449
|
dependencies = [
|
|
450
450
|
"axum",
|
|
451
451
|
"cap-std",
|
|
@@ -463,14 +463,14 @@ dependencies = [
|
|
|
463
463
|
|
|
464
464
|
[[package]]
|
|
465
465
|
name = "docxdriver-monty-repl"
|
|
466
|
-
version = "0.4.
|
|
466
|
+
version = "0.4.4"
|
|
467
467
|
dependencies = [
|
|
468
468
|
"docxdriver-core",
|
|
469
469
|
]
|
|
470
470
|
|
|
471
471
|
[[package]]
|
|
472
472
|
name = "docxdriver-python"
|
|
473
|
-
version = "0.4.
|
|
473
|
+
version = "0.4.4"
|
|
474
474
|
dependencies = [
|
|
475
475
|
"docxdriver-core",
|
|
476
476
|
"pyo3",
|
|
@@ -480,7 +480,7 @@ dependencies = [
|
|
|
480
480
|
|
|
481
481
|
[[package]]
|
|
482
482
|
name = "docxdriver-wasm"
|
|
483
|
-
version = "0.4.
|
|
483
|
+
version = "0.4.4"
|
|
484
484
|
dependencies = [
|
|
485
485
|
"docxdriver-core",
|
|
486
486
|
"js-sys",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docxdriver
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Python SDK for the native Rust DOCX engine
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -29,6 +29,7 @@ from docxdriver.plan import Plan, ReplaceText
|
|
|
29
29
|
doc = Document(Path("contract.docx").read_bytes())
|
|
30
30
|
doc.markup() # body with the ids plans address
|
|
31
31
|
doc.comments(); doc.revisions(); doc.styles(); doc.assets()
|
|
32
|
+
doc.bookmarks() # same as doc.read(kind="bookmarks")
|
|
32
33
|
doc.find("thirty")
|
|
33
34
|
review = doc.review(Plan(author="Reviewer", ops=[
|
|
34
35
|
ReplaceText(at="0F537164", select="thirty", with_="sixty"),
|
|
@@ -20,6 +20,7 @@ from docxdriver.plan import Plan, ReplaceText
|
|
|
20
20
|
doc = Document(Path("contract.docx").read_bytes())
|
|
21
21
|
doc.markup() # body with the ids plans address
|
|
22
22
|
doc.comments(); doc.revisions(); doc.styles(); doc.assets()
|
|
23
|
+
doc.bookmarks() # same as doc.read(kind="bookmarks")
|
|
23
24
|
doc.find("thirty")
|
|
24
25
|
review = doc.review(Plan(author="Reviewer", ops=[
|
|
25
26
|
ReplaceText(at="0F537164", select="thirty", with_="sixty"),
|
|
@@ -564,7 +564,20 @@ pub enum EditOp {
|
|
|
564
564
|
with: String,
|
|
565
565
|
},
|
|
566
566
|
EditTable { at: String, action: TableEdit },
|
|
567
|
-
|
|
567
|
+
/// A `rows` × `columns` grid of new cells. `cells` fills them row by
|
|
568
|
+
/// row with the inline content `insert_paragraph`'s `with` accepts;
|
|
569
|
+
/// missing trailing rows and cells stay empty. With `as`, cell row N
|
|
570
|
+
/// column M is the paragraph alias `$<as>_rNcM` for later operations.
|
|
571
|
+
InsertTable {
|
|
572
|
+
at: ParagraphAddress,
|
|
573
|
+
position: InsertPosition,
|
|
574
|
+
rows: u32,
|
|
575
|
+
columns: u32,
|
|
576
|
+
#[serde(default)]
|
|
577
|
+
cells: Option<Vec<Vec<String>>>,
|
|
578
|
+
#[serde(rename = "as", default)]
|
|
579
|
+
alias: Option<String>,
|
|
580
|
+
},
|
|
568
581
|
FormatTable { at: String, format: TableFormat },
|
|
569
582
|
FormatRow { at: String, format: RowFormat },
|
|
570
583
|
FormatCell { at: String, format: CellFormat },
|
|
@@ -748,6 +761,35 @@ pub enum EditOp {
|
|
|
748
761
|
CommentDelete {
|
|
749
762
|
comment_id: String,
|
|
750
763
|
},
|
|
764
|
+
/// Bookmark the paragraph at `at`: its whole content, or the `select`
|
|
765
|
+
/// span. Applied directly in both change modes (Word does not track
|
|
766
|
+
/// bookmarks). Body paragraphs only.
|
|
767
|
+
BookmarkAdd {
|
|
768
|
+
at: ParagraphAddress,
|
|
769
|
+
name: String,
|
|
770
|
+
#[serde(default)]
|
|
771
|
+
select: Option<String>,
|
|
772
|
+
#[serde(default)]
|
|
773
|
+
occurrence: Option<u32>,
|
|
774
|
+
},
|
|
775
|
+
/// Rename a body bookmark; `new_name` follows the add rules.
|
|
776
|
+
BookmarkRename {
|
|
777
|
+
name: String,
|
|
778
|
+
new_name: String,
|
|
779
|
+
},
|
|
780
|
+
/// Re-anchor a body bookmark, keeping its name and `w:id`.
|
|
781
|
+
BookmarkMove {
|
|
782
|
+
name: String,
|
|
783
|
+
at: ParagraphAddress,
|
|
784
|
+
#[serde(default)]
|
|
785
|
+
select: Option<String>,
|
|
786
|
+
#[serde(default)]
|
|
787
|
+
occurrence: Option<u32>,
|
|
788
|
+
},
|
|
789
|
+
/// Remove a body bookmark's start and end markers; the text stays.
|
|
790
|
+
BookmarkDelete {
|
|
791
|
+
name: String,
|
|
792
|
+
},
|
|
751
793
|
RevisionSettle {
|
|
752
794
|
target: RevisionTarget,
|
|
753
795
|
action: RevisionAction,
|
|
@@ -1260,7 +1302,11 @@ fn validate_plan(mut p: Plan) -> Result<Plan, Diagnostic> {
|
|
|
1260
1302
|
}
|
|
1261
1303
|
|
|
1262
1304
|
fn validation_field(message: &str) -> Option<&'static str> {
|
|
1263
|
-
let field = if message.
|
|
1305
|
+
let field = if message.starts_with("new_name ") {
|
|
1306
|
+
"new_name"
|
|
1307
|
+
} else if message.starts_with("name ") {
|
|
1308
|
+
"name"
|
|
1309
|
+
} else if message.contains("MathML") {
|
|
1264
1310
|
"mathml"
|
|
1265
1311
|
} else if message.contains("equation must") {
|
|
1266
1312
|
"equation"
|
|
@@ -1361,9 +1407,31 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1361
1407
|
}
|
|
1362
1408
|
}
|
|
1363
1409
|
EditOp::EditTable { at, action } => crate::commands::table_structure::validate(at, action)?,
|
|
1364
|
-
EditOp::InsertTable { rows, columns, alias, .. } => {
|
|
1410
|
+
EditOp::InsertTable { rows, columns, cells, alias, .. } => {
|
|
1365
1411
|
if *rows == 0 || *columns == 0 || *rows > 100 || *columns > 100 { return Err("insert_table dimensions must be 1..100".into()); }
|
|
1366
1412
|
if alias.as_ref().is_some_and(|a| !valid_alias(a)) { return Err("invalid alias".into()); }
|
|
1413
|
+
let cells = cells.as_deref().unwrap_or_default();
|
|
1414
|
+
if cells.len() > *rows as usize {
|
|
1415
|
+
let extra = cells.len() - *rows as usize;
|
|
1416
|
+
return Err(format!(
|
|
1417
|
+
"cells has {} rows but the table has {rows}: remove cells[{rows}..] (row {}{})",
|
|
1418
|
+
cells.len(),
|
|
1419
|
+
*rows + 1,
|
|
1420
|
+
if extra > 1 { format!(" to row {}", cells.len()) } else { String::new() },
|
|
1421
|
+
));
|
|
1422
|
+
}
|
|
1423
|
+
for (index, row) in cells.iter().enumerate() {
|
|
1424
|
+
if row.len() > *columns as usize {
|
|
1425
|
+
let extra = row.len() - *columns as usize;
|
|
1426
|
+
return Err(format!(
|
|
1427
|
+
"cells[{index}] (row {}) has {} cells but the table has {columns} columns: remove cells[{index}][{columns}..] (column {}{})",
|
|
1428
|
+
index + 1,
|
|
1429
|
+
row.len(),
|
|
1430
|
+
*columns + 1,
|
|
1431
|
+
if extra > 1 { format!(" to column {}", row.len()) } else { String::new() },
|
|
1432
|
+
));
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1367
1435
|
}
|
|
1368
1436
|
EditOp::FormatTable { at, format } => crate::commands::tables::validate_table(at, format)?,
|
|
1369
1437
|
EditOp::FormatRow { at, format } => crate::commands::tables::validate_row(at, format)?,
|
|
@@ -1566,6 +1634,31 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1566
1634
|
return Err("set_page_margins requires a margin".into());
|
|
1567
1635
|
}
|
|
1568
1636
|
}
|
|
1637
|
+
EditOp::BookmarkAdd {
|
|
1638
|
+
name,
|
|
1639
|
+
select,
|
|
1640
|
+
occurrence,
|
|
1641
|
+
..
|
|
1642
|
+
} => {
|
|
1643
|
+
crate::commands::bookmarks::validate_new_name(name, "name")?;
|
|
1644
|
+
validate_bookmark_select(select, occurrence)?;
|
|
1645
|
+
}
|
|
1646
|
+
EditOp::BookmarkMove {
|
|
1647
|
+
name,
|
|
1648
|
+
select,
|
|
1649
|
+
occurrence,
|
|
1650
|
+
..
|
|
1651
|
+
} => {
|
|
1652
|
+
crate::commands::bookmarks::validate_existing_name(name)?;
|
|
1653
|
+
validate_bookmark_select(select, occurrence)?;
|
|
1654
|
+
}
|
|
1655
|
+
EditOp::BookmarkRename { name, new_name } => {
|
|
1656
|
+
crate::commands::bookmarks::validate_existing_name(name)?;
|
|
1657
|
+
crate::commands::bookmarks::validate_new_name(new_name, "new_name")?;
|
|
1658
|
+
}
|
|
1659
|
+
EditOp::BookmarkDelete { name } => {
|
|
1660
|
+
crate::commands::bookmarks::validate_existing_name(name)?;
|
|
1661
|
+
}
|
|
1569
1662
|
EditOp::CommentAdd {
|
|
1570
1663
|
text,
|
|
1571
1664
|
occurrence,
|
|
@@ -1620,6 +1713,18 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1620
1713
|
}
|
|
1621
1714
|
Ok(())
|
|
1622
1715
|
}
|
|
1716
|
+
fn validate_bookmark_select(select: &Option<String>, occurrence: &Option<u32>) -> Result<(), String> {
|
|
1717
|
+
if select.as_deref() == Some("") {
|
|
1718
|
+
return Err("select must be non-empty; omit it to bookmark the whole paragraph".into());
|
|
1719
|
+
}
|
|
1720
|
+
if occurrence == &Some(0) {
|
|
1721
|
+
return Err("occurrence must be positive".into());
|
|
1722
|
+
}
|
|
1723
|
+
if occurrence.is_some() && select.is_none() {
|
|
1724
|
+
return Err("occurrence requires select".into());
|
|
1725
|
+
}
|
|
1726
|
+
Ok(())
|
|
1727
|
+
}
|
|
1623
1728
|
fn diag(code: impl Into<String>, message: impl Into<String>, path: Option<String>) -> Diagnostic {
|
|
1624
1729
|
Diagnostic {
|
|
1625
1730
|
code: code.into(),
|
|
@@ -1932,6 +2037,37 @@ fn fold_typed(
|
|
|
1932
2037
|
if matches!(op, EditOp::InsertTable { .. } | EditOp::EditTable { .. }) {
|
|
1933
2038
|
aliases.insert(alias.clone(), Value::String(format!("${alias}")));
|
|
1934
2039
|
}
|
|
2040
|
+
if let Some(rows) = outcome.result.as_ref().and_then(|r| r.get("cells")).and_then(Value::as_array) {
|
|
2041
|
+
let cells = rows.iter().enumerate().flat_map(|(r, row)| {
|
|
2042
|
+
row.as_array().into_iter().flatten().enumerate().map(move |(c, id)| (format!("{alias}_r{}c{}", r + 1, c + 1), id.clone()))
|
|
2043
|
+
});
|
|
2044
|
+
for (name, id) in cells {
|
|
2045
|
+
if aliases.contains_key(&name) {
|
|
2046
|
+
attach_affected(&mut state, &mut reports, &affected_ids);
|
|
2047
|
+
let report = PlanReport {
|
|
2048
|
+
completed: index,
|
|
2049
|
+
stopped_at: Some(index),
|
|
2050
|
+
ops: reports,
|
|
2051
|
+
repaired_ids: repaired_ids.clone(),
|
|
2052
|
+
allocated_ids: allocated_ids.clone(),
|
|
2053
|
+
aliases: aliases.clone().into_iter().collect(),
|
|
2054
|
+
};
|
|
2055
|
+
return (
|
|
2056
|
+
report.clone(),
|
|
2057
|
+
None,
|
|
2058
|
+
Some((
|
|
2059
|
+
diag(
|
|
2060
|
+
"duplicate_alias",
|
|
2061
|
+
format!("cell alias {name:?} of table alias {alias:?} is already defined"),
|
|
2062
|
+
Some(format!("ops[{index}].as")),
|
|
2063
|
+
),
|
|
2064
|
+
report,
|
|
2065
|
+
)),
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2068
|
+
aliases.insert(name, id);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
1935
2071
|
if let Some(id) = outcome
|
|
1936
2072
|
.result
|
|
1937
2073
|
.as_ref()
|
|
@@ -1942,6 +2078,12 @@ fn fold_typed(
|
|
|
1942
2078
|
aliases.insert(alias.clone(), id);
|
|
1943
2079
|
}
|
|
1944
2080
|
}
|
|
2081
|
+
if let EditOp::InsertTable { .. } = op {
|
|
2082
|
+
let rows = outcome.result.as_ref().and_then(|r| r.get("cells")).and_then(Value::as_array);
|
|
2083
|
+
for id in rows.into_iter().flatten().filter_map(Value::as_array).flatten() {
|
|
2084
|
+
allocated_ids.push(id.as_str().unwrap_or_default().into());
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1945
2087
|
}
|
|
1946
2088
|
attach_affected(&mut state, &mut reports, &affected_ids);
|
|
1947
2089
|
let report = PlanReport {
|
|
@@ -1992,6 +2134,16 @@ fn explain_selector_drift(
|
|
|
1992
2134
|
at,
|
|
1993
2135
|
select: Some(select),
|
|
1994
2136
|
..
|
|
2137
|
+
}
|
|
2138
|
+
| EditOp::BookmarkAdd {
|
|
2139
|
+
at,
|
|
2140
|
+
select: Some(select),
|
|
2141
|
+
..
|
|
2142
|
+
}
|
|
2143
|
+
| EditOp::BookmarkMove {
|
|
2144
|
+
at,
|
|
2145
|
+
select: Some(select),
|
|
2146
|
+
..
|
|
1995
2147
|
} => (at, vec![select.as_str()]),
|
|
1996
2148
|
EditOp::ReviseText {
|
|
1997
2149
|
at: Some(at),
|
|
@@ -2098,6 +2250,10 @@ fn operation_name(op: &EditOp) -> &'static str {
|
|
|
2098
2250
|
EditOp::CommentReply { .. } => "comment_reply",
|
|
2099
2251
|
EditOp::CommentSetStatus { .. } => "comment_set_status",
|
|
2100
2252
|
EditOp::CommentDelete { .. } => "comment_delete",
|
|
2253
|
+
EditOp::BookmarkAdd { .. } => "bookmark_add",
|
|
2254
|
+
EditOp::BookmarkRename { .. } => "bookmark_rename",
|
|
2255
|
+
EditOp::BookmarkMove { .. } => "bookmark_move",
|
|
2256
|
+
EditOp::BookmarkDelete { .. } => "bookmark_delete",
|
|
2101
2257
|
EditOp::RevisionSettle { .. } => "revision_settle",
|
|
2102
2258
|
}
|
|
2103
2259
|
}
|
|
@@ -2128,7 +2284,9 @@ fn resolve_typed_aliases(
|
|
|
2128
2284
|
| EditOp::InsertTable { at, .. }
|
|
2129
2285
|
| EditOp::SetSectionBoundary { at, .. }
|
|
2130
2286
|
| EditOp::FormatParagraph { at, .. }
|
|
2131
|
-
| EditOp::CommentAdd { at, .. }
|
|
2287
|
+
| EditOp::CommentAdd { at, .. }
|
|
2288
|
+
| EditOp::BookmarkAdd { at, .. }
|
|
2289
|
+
| EditOp::BookmarkMove { at, .. } => resolve(at)?,
|
|
2132
2290
|
EditOp::ReviseText { at: Some(at), .. } => resolve(at)?,
|
|
2133
2291
|
EditOp::InsertParagraph { at, .. } => {
|
|
2134
2292
|
if let InsertAnchor::Paragraph(addr) = at {
|
|
@@ -2478,6 +2636,11 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
|
|
|
2478
2636
|
"revisions",
|
|
2479
2637
|
source,
|
|
2480
2638
|
),
|
|
2639
|
+
"bookmarks" => typed_listing(
|
|
2640
|
+
crate::commands::bookmarks::list_bookmarks(&state.package, &state.xml, source),
|
|
2641
|
+
"bookmarks",
|
|
2642
|
+
source,
|
|
2643
|
+
),
|
|
2481
2644
|
other => crate::outcome::Outcome::error(format!("unknown read kind: {other}")),
|
|
2482
2645
|
}
|
|
2483
2646
|
}
|