[WebODF] Adding spec property "setOwnCursor" for Insert/Remove/Split ops

Philip Peitsch P.Peitsch at qsrinternational.com
Wed Jun 19 02:34:24 CEST 2013


Hi Friedrich,

The proposal looks good. 

Once concern that I did have is that there is potentially not enough flexibility. I'm thinking about table insertion specifically where the desired location of the cursor when inserting a table is actually within the first paragraph in the first cell of the table.

There are two potential ways to address this that immediately come to mind:

1. Make the setOwnCursor a little more flexible by allowing specific placement of the cursor at the completion of an operation. As long as the Ops respect the fact that setOwnCursor is RELATIVE to the input position, this should cause no hassles for OT adaption on conflict etc. (e.g., an Op should not set the cursor beyond the bounds of it's known neighbour hood, so for the table, it will insert the cursor within the first paragraph itself).

2. Keep cursor positioning a separate concern from the operation. The expectation here would be that an operation is NOT responsible for managing cursor position, instead, the upper controller (SessionController) is responsible for this. The key advantage of this is that it keeps the notion of cursor positioning entirely in the domain of the SessionController, rather than requiring each OP to find and manage it's own cursor (or even other cursors). I suspect this would help simplify the Ops slightly as they would focus more on doing things to the DOM, and would no longer need to worry about whether insertion is before or after the cursor etc.

In the example of a table insertion, approach 2 would generate:
OpInsertTable
OpMoveCursor (move to first paragraph of new table)

I *think* both approaches would work, but I prefer approach 2 as the SessionController seems like the correct place to manage cursor positioning. Especially as more complex OTs are developed :)

Cheers,

Philip
 
On 17/06/2013, at 8:39 PM, Friedrich W. H. Kossebau wrote:

> Hi WebODFler,
> 
> I would like to add a new property to the specs of all insert/remove/split 
> ops: setOwnCursor (or some better name). This property will tell if the cursor 
> of the member which sends the operation should be put after the place/result 
> of the op.
> Currently this is only done iff the cursor is at the position of the op. But 
> with real OT, where local operations are instantly applied and only then sent 
> to other clients who might have to transform them on conflicts, the 
> transformed operations and positions of cursors can be out-of-sync, breaking 
> the current assumption with UI editing that cursors will always automatically 
> placed behind the places of modification.
> 
> Example:
> <text:p><cursor memberid="A"><cursor memberid="B"></text:p>
> Both clients A and B type at the same time "a" and "b" and apply it locally, 
> before sending to server/other client:
> At A:
> <text:p><cursor memberid="B">a<cursor memberid="A"></text:p>
> Unsynced ops: [<insertText pos="0" text="a" memberId="A"/>]
> At B:
> <text:p><cursor memberid="A">b<cursor memberid="B"></text:p>
> Unsynced ops: [<insertText pos="0" text="b" memberId="B"/>]
> At server:
> op stack: []
> As this is a case for tiebreaking on conflicting operations, because both 
> insert at the same position and it needs to be decided in which order the 
> insertions will end up in the final document, we assume that
> * priority is given to ops which are first on the server
> * priority in this case means insertion will be placed before the other 
> insertion
> A is quicker with syncing with server:
> At A:
> <text:p><cursor memberid="B">a<cursor memberid="A"></text:p>
> Unsynced ops: []
> At server:
> op stack: [<insertText pos="0" text="a" memberId="A"/>]
> Now B tries to sync, but sees that there are conflicting ops, which it first 
> has to resolve locally, by transforming its local unsynced op and the new on 
> the server against each other, so the server op is based on the state of B and 
> the unsynced op is based on the (virtual) state of the server:
> T(<insertText pos="1" text="b" memberId="B"/>,
>  <insertText pos="0" text="a" memberId="A"/> )
> ->
> (<insertText pos="1" text="b" memberId="B"/>,
> <insertText pos="0" text="a" memberId="A"/> )
> given the tiebreaking rules above.
> After transformation B then applies the transformed op from the server and 
> sends its own transformed unsynced op to the server
> At B:
> <text:p>a<cursor memberid="A">b<cursor memberid="B"></text:p>
> Unsynced ops: []
> At server:
> op stack: [<insertText pos="0" text="a" memberId="A"/>,
>           <insertText pos="1" text="b" memberId="B"/>]
> Now A syncs again with the server, fetching the new op.
> And here we are with the problem: At A the cursor of B is still at the 
> position 0, while the operation says that B inserts at position 1. Still we 
> want to have the cursor of B after the op to be at position 3 (not only for 
> convergence of the 2 clients).
> 
> I would like to solve that by allowing ops to optionally take the cursor from 
> whereever it was and just move it behind the result of the operation, instead 
> of always only moving it if it is at the position of the operation.
> This would also allow to explicitely do operations at the current cursor 
> position without moving the cursor, which currently is not possible.
> 
> Operation creators would specify the cursor movement by setting the new 
> property "setOwnCursor" to "false" or "true". It would be added to all 
> operations where the text structure is changed.
> 
> Any blockers, counterproposals, comments, icecoffee recipes?
> 
> Cheers
> Friedrich
> -- 
> Friedrich W. H. Kossebau // KO GmbH
> http://kogmbh.com/legal/
> _______________________________________________
> WebODF mailing list
> WebODF at nlnet.nl
> https://open.nlnet.nl/mailman/listinfo/webodf



More information about the WebODF mailing list