okay after tons of printf debugging, here’s how things were going haywire. don’t blame Glitch, it was a bug in my code.
the Glitch editor, when you press enter on an indented line, as was the case in your HTML use case, creates a new line and indents it appropriately.
when it tells the Glitch OT server about the new indented line, it doesn’t insert it as “newline, space, space, …”. it first inserts just the newline, then in a second operation, it inserts the indentation spaces.
my code would handle an incoming list of operations (an “oplist”) by increasing the version number and then examining each operation in sequence.
when my code examines an operation that finishes one or more lines, it invokes the callback for handling a new line, synchronously. but in this case, it hasn’t even finished processing the oplist.
the synchronous part of the line handler starts sending out a new transaction, mistakenly thinking it is already operating on the new version, as the version number has already been increased.
then my code finally gets to the second part of the incoming oplist, and it incorrectly sees this a something that needs to fix up the outstanding transaction.
so as a result,
an oplist goes out to the OT server that tries to operate on the wrong starting state.
the client’s view of the outstanding transaction incorrectly gets updated, which later causes the successor transaction to be created with the wrong information too
basically everything thereafter goes wrong because of the above
I’ve made the following fixes in the original maple-separated-seal:
Move the version increment to the end of the oplist apply-er:
function opListApply(opList) {
- version = opList.version + 1;
for (const op of opList.ops) {
docsVisitOp(op);
txsVisitOp(op);
readerVisitOp(op);
}
+ version = opList.version + 1;
}
this is a safeguard to make any transactions sent while in the middle of processing an incoming oplist fail on the server. okay and I just realized that I had not settled whether oplist is one word or two words together, as opList.
wait for a microtask at the top of readerVisitLine
function readerVisitLine(docId, unsubmittedTx) {
+ (async () => {
+ try {
+ // wait for a microtask, after we finish processing the whole oplist that
+ // this line appeared in.
+ await Promise.resolve();
+
if (!unsubmittedTx.tx) throw unsubmittedTx.reason;
...
const submitTxPromise = submitUnsubmittedTx(unsubmittedTx);
- (async () => {
- try {
const unsubmittedTx2 = await submitTxPromise;
this prevents us from submitting a transaction until after the whole incoming oplist is processed. note that the first if (!unsubmittedTx) throw is now required, as an oplist may create a line and then disturb it in the same version, before we finish awaiting that Promise.resolve().
Magic spaces were part of the problem. Using your new code:
Magic spacing corresponding to the indent appears! That was part of the problem if you had no indent (Or a lot of unneeded indent), as that spacing can possibly cause issues.
I’ll update my bot to the new code but now I need to add even more code to handle the dumb spaces.
EDIT: New code makes it even harder to work around the dumb space issue >:/
I’m going to stay on the old codebase in order to keep the workaround for now
It would be nice if the indent worked right, currently it only spawns the indent the editor expects (Which often doesn’t work with extra / removed indents)
but there’s a part where it sets the relative offset to end, meaning to insert the text right after the newline. you would instead use end - 1, so that it gets inserted before the newline. and with that, don’t insert any newlines, because that would make it look like the same line has been created again