Skip to content

Commit ae01ce2

Browse files
committed
chore: archive cross-boundary text operations proposal
1 parent 93cee33 commit ae01ce2

5 files changed

Lines changed: 102 additions & 0 deletions

File tree

openspec/changes/add-cross-boundary-text-operations/design.md renamed to openspec/changes/archive/2026-01-29-add-cross-boundary-text-operations/design.md

File renamed without changes.

openspec/changes/add-cross-boundary-text-operations/proposal.md renamed to openspec/changes/archive/2026-01-29-add-cross-boundary-text-operations/proposal.md

File renamed without changes.

openspec/changes/add-cross-boundary-text-operations/specs/text-operations/spec.md renamed to openspec/changes/archive/2026-01-29-add-cross-boundary-text-operations/specs/text-operations/spec.md

File renamed without changes.

openspec/changes/add-cross-boundary-text-operations/tasks.md renamed to openspec/changes/archive/2026-01-29-add-cross-boundary-text-operations/tasks.md

File renamed without changes.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# text-operations Specification
2+
3+
## Purpose
4+
TBD - created by archiving change add-cross-boundary-text-operations. Update Purpose after archive.
5+
## Requirements
6+
### Requirement: Virtual Text Map
7+
8+
The system SHALL provide a flattened text view of document content that maps character positions back to their source XML elements.
9+
10+
The text map SHALL:
11+
- Concatenate all visible text from `<w:t>` elements within a paragraph
12+
- Exclude text inside `<w:delText>` elements (deleted content)
13+
- Track whether each character position is inside a `<w:ins>` or `<w:del>` element
14+
- Map each character position to its source `<w:t>` node and offset
15+
16+
#### Scenario: Build text map for paragraph with tracked changes
17+
18+
- **GIVEN** a paragraph containing: regular text "Hello ", inserted text "beautiful ", regular text "world"
19+
- **WHEN** `build_text_map()` is called on the paragraph
20+
- **THEN** the text map contains "Hello beautiful world"
21+
- **AND** positions 0-5 map to the first `<w:t>` with `is_inside_ins=False`
22+
- **AND** positions 6-15 map to the second `<w:t>` with `is_inside_ins=True`
23+
- **AND** positions 16-20 map to the third `<w:t>` with `is_inside_ins=False`
24+
25+
#### Scenario: Deleted text excluded from visible text
26+
27+
- **GIVEN** a paragraph containing: "Hello " and deleted text "old " and "world"
28+
- **WHEN** `build_text_map()` is called
29+
- **THEN** the text map contains "Hello world" (deleted text excluded)
30+
31+
### Requirement: Cross-Boundary Text Search
32+
33+
The system SHALL find text that spans multiple XML elements within a paragraph.
34+
35+
#### Scenario: Search finds text spanning element boundary
36+
37+
- **GIVEN** a paragraph with "Exploratory Aim: " in one `<w:t>` and "To examine" in another
38+
- **WHEN** searching for "Aim: To"
39+
- **THEN** the search succeeds and returns match information
40+
- **AND** the match indicates it spans multiple elements
41+
42+
#### Scenario: Search finds text spanning insertion boundary
43+
44+
- **GIVEN** a paragraph with "Hello " as regular text and "world" inside `<w:ins>`
45+
- **WHEN** searching for "Hello world"
46+
- **THEN** the search succeeds
47+
- **AND** the match indicates it spans a revision boundary
48+
49+
### Requirement: Visible Text API
50+
51+
The system SHALL provide a public API to retrieve the flattened visible text of a document.
52+
53+
#### Scenario: Get visible text from document
54+
55+
- **GIVEN** a document with paragraphs containing mixed regular and tracked-change content
56+
- **WHEN** `get_visible_text()` is called
57+
- **THEN** the method returns a string containing all visible text
58+
- **AND** deleted text is excluded
59+
- **AND** inserted text is included
60+
61+
### Requirement: Boundary-Aware Text Replacement
62+
63+
The system SHALL replace text that spans multiple `<w:t>` elements within the same revision context, using proper node splitting.
64+
65+
#### Scenario: Replace text spanning multiple runs
66+
67+
- **GIVEN** a paragraph with "Hello " in one run and "world" in another (no revisions)
68+
- **WHEN** `replace_text("Hello world", "Hi there")` is called
69+
- **THEN** the replacement succeeds
70+
- **AND** the original text is wrapped in `<w:del>`
71+
- **AND** the new text is wrapped in `<w:ins>`
72+
73+
### Requirement: Mixed-State Editing
74+
75+
The system SHALL replace text that spans revision boundaries by decomposing the operation into per-segment atomic actions, one for each revision context (regular text, inside `<w:ins>`, inside `<w:del>`).
76+
77+
#### Scenario: Replace spanning regular text and insertion
78+
79+
- **GIVEN** a paragraph with "Exploratory Aim: " as regular text and "To examine" inside `<w:ins>`
80+
- **WHEN** `replace_text("Aim: To", "Goal: To")` is called
81+
- **THEN** the replacement succeeds
82+
- **AND** "Aim: " is wrapped in `<w:del>` (standard deletion of regular text)
83+
- **AND** "To" is removed from the `<w:ins>` element (undoing partial insertion)
84+
- **AND** "Goal: To" is inserted as a new `<w:ins>` element
85+
- **AND** the remaining " examine" stays inside the original `<w:ins>`
86+
87+
#### Scenario: Replace fully within insertion
88+
89+
- **GIVEN** a paragraph with "Hello beautiful world" entirely inside `<w:ins>`
90+
- **WHEN** `replace_text("beautiful", "wonderful")` is called
91+
- **THEN** the replacement succeeds
92+
- **AND** "beautiful" is removed from the insertion (undoing partial insertion)
93+
- **AND** "wonderful" is inserted as a new `<w:ins>` element
94+
95+
#### Scenario: Insertion node is split when partially matched
96+
97+
- **GIVEN** a paragraph with `<w:ins>To examine whether</w:ins>`
98+
- **WHEN** a delete operation targets "To" (the first 2 characters)
99+
- **THEN** the `<w:ins>` is split into two parts
100+
- **AND** the `<w:ins>To</w:ins>` portion is removed
101+
- **AND** `<w:ins> examine whether</w:ins>` remains intact
102+

0 commit comments

Comments
 (0)