Skip to content

Commit e7651c4

Browse files
author
Satvik Kumar
committed
List Editing Design
1 parent 556e58e commit e7651c4

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

webodf/README_lists.md

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
# List Editing
2+
3+
At its most basic level this list editing feature is about manipulating `text:list` elements in a document. These elements are defined here in the ODF standard http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1415148_253892949
4+
5+
Possible elements of a list:
6+
```xml
7+
<text:list text:continue-list="" text:continue-numbering="" text:style-name="" xml:id="">
8+
<text:list-header xml:id="">
9+
<text:h/>
10+
<text:list/>
11+
<text:number/>
12+
<text:p/>
13+
<text:soft-page-break/>
14+
</text:list-header>
15+
<text:list-item text:start-value="" text:style-override="" xml:id="">
16+
<text:h/>
17+
<text:list/>
18+
<text:number/>
19+
<text:p/>
20+
<text:soft-page-break/>
21+
</text:list-item>
22+
</text:list>
23+
```
24+
Possible elements of a list style:
25+
```xml
26+
<text:list-style style:display-name="" style:name="" text:consecutive-numbering="">
27+
<text:list-level-style-bullet/>
28+
<text:list-level-style-image/>
29+
<text:list-level-style-number/>
30+
</text:list-style>
31+
```
32+
33+
## Problem/Goal
34+
35+
This section will define what it is we are trying to solve by implementing list editing and what information needs to be considered for the design of the operations that make list editing changes in the document.
36+
37+
### Creating a list from non-list paragraphs
38+
This could be a new list in the document or if created from paragraphs that occur directly after an existing list in the document then this new list is merged into the previous list if their styles match. Any existing list items that are part of the selection are also merged into the new list.
39+
40+
### Turn list items back into non-list paragraphs
41+
If the selection covers an entire top level list then that whole list is removed. If the selection only covers a subset of a top level list then that list will need to be split.
42+
43+
### Inserting text to create list items
44+
Inserting a line break and then additional text anywhere in an existing list causes a new list item to be created. If there was text after the line break then this becomes a part of the new list item which in essence splits the list item. This is already implemented by WebODF.
45+
46+
Inserting two line breaks in other editors causes a list to be terminated and any further text inserted at that point is part of a non-list paragraph. This has not been implemented.
47+
48+
### Removing list items by deleting text
49+
Removing all the text from a list item leaves behind a list item containing an empty paragraph. Removing the line break at the start of a list item merges it into the previous list item much like normal paragraph editing. These actions are already implemented by WebODF.
50+
51+
Removing the empty paragraph in a list item causes the list item to be deleted and the top level list to be split at that point. This has not been implemented.
52+
53+
### Change the style of a list
54+
In a selection that contains existing list items this changes the `text:list-style` used for the list items. If this is a partial selection of a top level list then the list will be split. Any non-list paragraphs will be merged into the modified list.
55+
56+
### Promote/Demote a list item
57+
Since lists can be hierarchical we can move list items up or down a level in the hierarchy. In most editors other than LibreOffice this only works within a single top level list. LibreOffice differs in that it allows shifting the level of list items across multiple lists.
58+
59+
### Insert multiple paragraphs into a single list item
60+
This is usually done by the shortcut Shift-Enter which creates a new paragraph enclosed by the list item of the previous paragraph.
61+
62+
### Changing a list item to a list header
63+
This is supported in LibreOffice but doesn't exist for other editors. A list header is a list item that does not have any kind of list label.
64+
65+
### Continuing the numbering of the previous list in the document
66+
The `text:continue-numbering` attribute specifies whether to continue the numbering of a top level list that directly precedes it in the document. The `text:continue-id` attribute specifies the `xml:id` of some previous list to continue in the document.
67+
68+
### Restart list numbering
69+
In numbered lists the `text:start-value` attribute on a list item specifies the value the numbering should start at. This is generally used to restart the list numbering at the number 1.
70+
71+
### General concerns
72+
The current WebODF addressing system deals with text positions and `text:list` elements don't add extra walk-able steps to the document. This means that we can only handle lists based on the paragraphs they contain. This is ok as a top level list that does not contain a paragraph can be considered an invalid construct. However in a nested list structure some `text:list` elements may not have any paragraph elements directly contained by their child list items. This can be seen below.
73+
74+
```xml
75+
<text:list>
76+
<text:list-item>
77+
<text:p>Level 1</text:p>
78+
<text:list>
79+
<text:list-item>
80+
<text:list>
81+
<text:list-item>
82+
<text:p>Level 3</text:p>
83+
</text:list-item>
84+
</text:list>
85+
</text:list-item>
86+
</text:list>
87+
</text:list-item>
88+
</text:list>
89+
```
90+
91+
In this example the second level `text:list` element has no paragraph so it can't be identified directly with a step co-ordinate. This limits us to using such a co-ordinate to identify the top level list in the document.
92+
93+
There are other ways to identify a list such as using an `xml:id` attribute but this is not useful positional information for operations or op transforms.
94+
95+
As mentioned already adding a list to the document doesn't add more content and also does not reorder or shift paragraphs around. This is a useful property and needs to be preserved especially for lists inside other elements such as tables and floating frames.
96+
97+
## Operations
98+
The design for these operations was done with the recommendations from this [document](https://github.com/kogmbh/WebODF/blob/master/webodf/README_operationaltransforms.md) in mind.
99+
Following other operations that deal with paragraphs such as OpMergeParagraph all step positions that identify paragraph positions must be the first step in the paragraph. A further extension of this concept is that a list in a document will be identified by the first step in the first paragraph of the list.
100+
101+
### OpAddList
102+
This operation takes a range of paragraphs and converts them into a list. This will create a new top level list at the point of the first paragraph in the range. Each paragraph in the range will become a list item in the new list. The op should reject any range of paragraphs that do not have the same parent. This is to prevent document contents changing order. The new list should have an `xml:id` but this is not a part of this op.
103+
104+
#### Spec
105+
* startParagraphPosition
106+
* endParagraphPosition
107+
* styleName - optional
108+
109+
### OpRemoveList
110+
This operation takes the position of a top level list and converts all the list items back to non-list paragraphs. This will not handle dealing with any complications arising from continued list numbering or a deleted `xml:id`.
111+
112+
#### Spec
113+
* firstParagraphPosition
114+
* lastParagraphPosition - this may only be needed to assist OT by defining list bounds
115+
116+
### OpSplitList
117+
This operation takes the position of a top level list and also a paragraph position to split the list at. This paragraph becomes the new first paragraph of the split list. Splitting the list does not affect the structure of hierarchical lists. Splitting a list clones the attributes on the top level list into the new list created as part of the split.
118+
119+
#### Spec
120+
* sourceStartPosition
121+
* splitPosition
122+
123+
### OpMergeList
124+
This operation is similar to OpMergeParagraph. It takes two adjacent top level lists in the document and merges them into one list. Lists that are not adjacent can not be merged. The list specified by the sourceStartPosition will be merged into the list identified by destinationStartPosition. Lists can't be merged in the other direction. Merging a list does not affect the structure of hierarchical lists. Merging a list is similar to removing a list but any continued list or `xml:id` concerns are not handled by this OP.
125+
126+
#### Spec
127+
* sourceStartPosition
128+
* destinationStartPosition
129+
130+
### OpSetListProperties
131+
This operation sets the properties as attributes on the top level list element identified by firstParagraphPosition. This can be used to set things like the `xml:id` or the `text:continue-id` on a list.
132+
133+
#### Spec
134+
* firstParagraphPosition
135+
* listProperties
136+
137+
### OpPromoteListItems
138+
This operation takes a range of paragraphs which are part of a list to promote to a higher list level in the hierarchy. This means that any list item at list level 2 is now at list level 1. All paragraphs in the range must be part of the list identified by listPosition. All paragraphs in the range must be able to be promoted to the next list level. This means that any range with paragraphs already at list level 1 cannot be promoted further.
139+
140+
#### Spec
141+
* listPosition
142+
* startParagraphPosition
143+
* endParagraphPosition
144+
145+
### OpDemoteListItems
146+
This operation takes a range of paragraphs which are part of a list to demote to a lower list level in the hierarchy. This means that any list item at list level 2 is now at list level 3. All paragraphs in the range must be part of the list identified by listPosition. All paragraphs in the range must be able to be demoted to the next list level. This means that any range with paragraphs already at list level 10 cannot be demoted further.
147+
148+
#### Spec
149+
* listPosition
150+
* startParagraphPosition
151+
* endParagraphPosition
152+
153+
## Scenarios
154+
This section aims to provide some examples as to how the proposed operations solve some of the list editing actions identified earlier.
155+
156+
### Adding list items
157+
The most basic action is converting a set of paragraphs so that they become a list. This is achieved by using OpAddList only. A more interesting scenario is when the selected paragraphs are already part of a list as illustrated below.
158+
159+
```xml
160+
<text:p>Test</text:p>
161+
<text:p>Test</text:p>
162+
<text:list text:style-name="styleA">
163+
<text:list-item>
164+
<text:p>Level 1</text:p>
165+
<text:list>
166+
<text:list-item>
167+
<text:p>Level 2</text:p>
168+
<text:list>
169+
<text:list-item>
170+
<text:p>Level 3</text:p>
171+
</text:list-item>
172+
</text:list>
173+
</text:list-item>
174+
</text:list>
175+
</text:list-item>
176+
</text:list>
177+
```
178+
179+
Assuming a selection covering the first two paragraphs and the first two list items being converted into a new list with the style "styleB" the desired result is as follows.
180+
181+
```xml
182+
<text:list text:style-name="styleB">
183+
<text:list-item>
184+
<text:p>Test</text:p>
185+
</text:list-item>
186+
<text:list-item>
187+
<text:p>Test</text:p>
188+
</text:list-item>
189+
<text:list-item>
190+
<text:p>Level 1</text:p>
191+
<text:list>
192+
<text:list-item>
193+
<text:p>Level 2</text:p>
194+
</text:list-item>
195+
</text:list>
196+
</text:list-item>
197+
</text:list>
198+
<text:list text:style-name="styleA">
199+
<text:list-item>
200+
<text:list>
201+
<text:list-item>
202+
<text:list>
203+
<text:list-item>
204+
<text:p>Level 3</text:p>
205+
</text:list-item>
206+
</text:list>
207+
</text:list-item>
208+
</text:list>
209+
</text:list-item>
210+
</text:list>
211+
```
212+
213+
The sequence of operations required for this is to firstly do an OpAddList on the non-list paragraphs to create a new list with the style name "styleB". Since the selection then intersects with existing items we must then split them out into their own top level list so we can work with them. This is done with an OpSplitList at the paragraph containing "Level 3". Then an OpMergeList is done to join the new list with the top part of the split list.
214+
215+
### Removing list items
216+
The most basic list removal scenario is to remove all the list items in a list which can be done by only using OpRemoveList. A more difficult scenario is to partially remove a list shown below.
217+
218+
```xml
219+
<text:list text:style-name="styleA">
220+
<text:list-item>
221+
<text:p>Level 1</text:p>
222+
<text:list>
223+
<text:list-item>
224+
<text:p>Level 2</text:p>
225+
<text:list>
226+
<text:list-item>
227+
<text:p>Level 3</text:p>
228+
<text:list>
229+
<text:list-item>
230+
<text:p>Level 4</text:p>
231+
</text:list-item>
232+
</text:list>
233+
</text:list-item>
234+
</text:list>
235+
</text:list-item>
236+
</text:list>
237+
</text:list-item>
238+
</text:list>
239+
```
240+
241+
Assuming a selection that removes the paragraphs "Level 2" and "Level 3" the desired result is as follows.
242+
243+
```xml
244+
<text:list text:style-name="styleA">
245+
<text:list-item>
246+
<text:p>Level 1</text:p>
247+
</text:list-item>
248+
</text:list>
249+
<text:p>Level 2</text:p>
250+
<text:p>Level 3</text:p>
251+
<text:list text:style-name="styleA">
252+
<text:list-item>
253+
<text:list>
254+
<text:list-item>
255+
<text:list>
256+
<text:list-item>
257+
<text:list>
258+
<text:list-item>
259+
<text:p>Level 4</text:p>
260+
</text:list-item>
261+
</text:list>
262+
</text:list-item>
263+
</text:list>
264+
</text:list-item>
265+
</text:list>
266+
</text:list-item>
267+
</text:list>
268+
```
269+
270+
The sequence of ops here is to do an OpSplitList at the paragraph "Level 2" and then another split list at the "Level 4" paragraph. This should leave us with three lists with the middle list containing the paragraphs "Level 2" and "Level 3". An OpRemoveList can then be done on this middle list to convert this back to non-list paragraphs.
271+
272+
### Changing style of list items
273+
Reusing the example from before instead of removing the paragraphs "Level 2" and "Level 3" we will instead change the list style applied to them. The desired result is below.
274+
275+
```xml
276+
<text:list text:style-name="styleA">
277+
<text:list-item>
278+
<text:p>Level 1</text:p>
279+
</text:list-item>
280+
</text:list>
281+
<text:list text:style-name="styleB">
282+
<text:list-item>
283+
<text:list>
284+
<text:list-item>
285+
<text:p>Level 2</text:p>
286+
<text:list>
287+
<text:list-item>
288+
<text:p>Level 3</text:p>
289+
</text:list-item>
290+
</text:list>
291+
</text:list-item>
292+
</text:list>
293+
</text:list-item>
294+
</text:list>
295+
<text:list text:style-name="styleA">
296+
<text:list-item>
297+
<text:list>
298+
<text:list-item>
299+
<text:list>
300+
<text:list-item>
301+
<text:list>
302+
<text:list-item>
303+
<text:p>Level 4</text:p>
304+
</text:list-item>
305+
</text:list>
306+
</text:list-item>
307+
</text:list>
308+
</text:list-item>
309+
</text:list>
310+
</text:list-item>
311+
</text:list>
312+
```
313+
314+
The sequence of ops is also similar starting with the same usage of OpSplitList resulting in three lists. However instead of doing an OpRemoveList we do an OpSetListProperties on the middle list to apply the new style to it.

0 commit comments

Comments
 (0)