Simulators
Ranges and insertOoxml
An insertOoxml moves ranges by rules of its own, apart from insertText and delete (Ranges and collections). It puts every range that moves with the text back where it sat when your add-in last reached it:
await Word.run(async context => {
context.document.body.insertParagraph('aaaa bbbb cccc dddd eeee', 'End');
await context.sync();
});
await Word.run(async context => {
const body = context.document.body;
const paragraph = body.paragraphs.getLast();
const hits = ['aaaa', 'cccc', 'eeee'].map(word => body.search(word).getFirst());
context.document.changeTrackingMode = Word.ChangeTrackingMode.off; // a write, so the hits move with the text
await context.sync();
paragraph.insertText('ZZ', 'Start');
const pkg = paragraph.getOoxml();
await context.sync();
body.insertOoxml(pkg.value, 'End');
hits.forEach(hit => hit.load('text'));
await context.sync();
// hits.map(hit => hit.text) -> ["ZZaa", "b cc", "d ee"]
});
An insertOoxml puts back each range your add-in hasn’t reached since it moved
Word settles where a range sits when your add-in reaches it. Your add-in reaches a range when it:
- makes it, reads it or calls a member on it;
- passes it to a member as an argument;
- takes it with
getFirst; - edits through it.
Loading a collection that holds the range doesn’t reach it.
An insertOoxml anywhere in the document puts every range that moves with the text back where it sat when last reached. That holds through the body, a paragraph or a range, at any location, tracked or not. The moves since go, an accept’s or a reject’s included. In the example above, insertText('ZZ', 'Start') moved the hits, and the package put them back. A hit a newline carried into a new paragraph goes back to the first part.
A delete in the paragraph goes the same way for its ranges. On aaaa bbbb cccc dddd eeee, a hit on cccc that deleting bbbb cccc collapsed reads eee once a package goes in.
Two kinds of range keep their place:
- the range an
insertText,delete, the body’sinsertParagraphorinsertOoxmlwent through, which stays where that edit put it; - a range your add-in read or called anything on in between.
insertText, the body’s insertParagraph, delete and getOoxml put nothing back. To rely on a range across an insertOoxml, read it after the edits that moved it.
The paragraph a package splits
insertOoxml’s own text moves no range but the one it went through. A package of several paragraphs, or one holding a table, splits the paragraph it lands in. A package that starts with a table splits nothing off at the paragraph’s start (below). The paragraph’s Paragraph object keeps its offsets in the last part, the one holding the paragraph’s mark. So does every other range in it, and every tracked change listed before.
Take the paragraph aaaa bbbb cccc dddd eeee and an earlier hit on cccc. After a package of Q1 and Q2 goes in before bbbb, the hit reads c dd.
A tracked change listed before the split then settles by the offsets it kept:
| Settling | Result |
|---|---|
| accepting an insertion, rejecting a deletion or settling a formatting change | settles each change of its kind whose text lies wholly inside those offsets |
| rejecting an insertion or accepting a deletion | takes out the text there, short of where its mark was |
A paragraph mark inside those offsets has its revision of the change’s kind settled too, whoever made it. Where that settle would take the mark out, the mark stays with the revision dropped. The exception is a change that held its mark at that very offset: there the paragraph joins the next. Rejecting an insertion, or accepting a deletion, that held its mark elsewhere also throws GeneralException.
A package that starts or ends with a table
At a paragraph’s start, a package that starts with a table splits nothing off. The table goes in right before the paragraph, which keeps its object, its mark and every range in it. A table already right before it, of the same layout and table style, takes the package table’s rows. Each row keeps, in a w:tblPrEx, the alignment, borders, shading, cell spacing and margins its own table differed in.
Differs from Word for the web: a package table that joins the table before it drops a table style no part defines, and Word writes its indent as grid offsets. The simulator keeps the style, keeps the tables apart and drops the indent.
A range a table-first package went through at its Start starts at the table, there or inside the text. A hit on bbbb reads T1\rbbbb. A collapsed range at its End grows over the package too.
A range that ends where a package ending in a table ends goes past the table’s last cell mark. That is the range insertOoxml returns, or one it went through. So text inserted at its End lands in that cell. Tables side by side in a package join as one.
A package at a whole range’s End
A range can end past its paragraph’s mark, as getRange('Whole') does. A package of several paragraphs that insertOoxml puts at that range’s End splits the paragraph. It ends the range at the last part’s text, short of the mark, tracked or not. Other ranges in the paragraph then read:
| Range | Reads, for a package of a and b |
|---|---|
one that kept its offsets, and the getRange('Whole') and getRange('Content') taken from it | one paragraph mark more after the first part, …a\r\rb, until an edit goes through it |
| one that moves with the text | …a\rb |
The document holds no paragraph at that extra mark. A package that starts with a table leaves the range over the package’s content: T1\rQ2 for a table cell T1 and a paragraph Q2.
A range a delete collapsed
An untracked delete() of a paragraph collapses some of its ranges to the start of the next paragraph (Ranges and collections). Until your add-in reaches such a range again, an insertOoxml can make it throw ItemNotFound:
| Range | Throws after an insertOoxml that lands |
|---|---|
| one collapsed to the next paragraph’s start | in that paragraph, at any location |
| one that ran out of, into or across the deleted paragraph | anywhere |
| one the document’s last paragraph held | anywhere |
A package inserted through the collapsed range itself lands at that paragraph’s start. The range holds it at Start, End and Replace.
A range a delete() collapsed to the next paragraph’s start, not reached since, reads empty after an insertOoxml of several paragraphs into the paragraph after that one.
Differs from Word for the web: after most such insertOoxml calls, Word makes the range throw ItemNotFound, and it reads empty only after the rest.