Skip to content

Commit

Permalink
TODO: add commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuyz0112 committed Aug 16, 2020
1 parent 5e9e1a2 commit eb352be
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 12 deletions.
133 changes: 122 additions & 11 deletions src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,88 @@ import {
} from '../types';
import { mirror, isBlocked, isAncestorRemoved } from '../utils';

type DoubleLinkedListNode = {
previous: DoubleLinkedListNode | null;
next: DoubleLinkedListNode | null;
value: NodeInLinkedList;
};
type NodeInLinkedList = Node & {
__ln: DoubleLinkedListNode;
};

function isNodeInLinkedList(n: Node | NodeInLinkedList): n is NodeInLinkedList {
return '__ln' in n;
}
class DoubleLinkedList {
public length = 0;
public head: DoubleLinkedListNode | null = null;

public get(position: number) {
if (position >= this.length) {
throw new Error('Position outside of list range');
}

let current = this.head;
for (let index = 0; index < position; index++) {
current = current?.next || null;
}
return current;
}

public addNode(n: Node) {
const node: DoubleLinkedListNode = {
value: n as NodeInLinkedList,
previous: null,
next: null,
};
(n as NodeInLinkedList).__ln = node;
if (n.previousSibling && isNodeInLinkedList(n.previousSibling)) {
const current = n.previousSibling.__ln.next;
node.next = current;
node.previous = n.previousSibling.__ln;
n.previousSibling.__ln.next = node;
if (current) {
current.previous = node;
}
} else if (n.nextSibling && isNodeInLinkedList(n.nextSibling)) {
const current = n.nextSibling.__ln.previous;
node.previous = current;
node.next = n.nextSibling.__ln;
n.nextSibling.__ln.previous = node;
if (current) {
current.next = node;
}
} else {
if (this.head) {
this.head.previous = node;
}
node.next = this.head;
this.head = node;
}
this.length++;
}

public removeNode(n: NodeInLinkedList) {
const current = n.__ln;
if (!this.head) {
return;
}

if (!current.previous) {
this.head = current.next;
if (this.head) {
this.head.previous = null;
}
} else {
current.previous.next = current.next;
if (current.next) {
current.next.previous = current.previous;
}
}
this.length--;
}
}

const moveKey = (id: number, parentId: number) => `${id}@${parentId}`;
function isINode(n: Node | INode): n is INode {
return '__sn' in n;
Expand Down Expand Up @@ -70,22 +152,29 @@ export default class MutationBuffer {
}

public processMutations = (mutations: mutationRecord[]) => {
mutations.forEach(this.processMutation);
mutations.reverse().forEach(this.processMutation);

/**
* Sometimes child node may be pushed before its newly added
* parent, so we init a queue to store these nodes.
*/
const addQueue: Node[] = [];
const addQueue = new DoubleLinkedList();
const getNextId = (n: Node): number | null => {
let nextId =
n.nextSibling && mirror.getId((n.nextSibling as unknown) as INode);
if (nextId === -1 && isBlocked(n.nextSibling, this.blockClass)) {
nextId = null;
}
return nextId;
};
const pushAdd = (n: Node) => {
if (!n.parentNode) {
return;
}
const parentId = mirror.getId((n.parentNode as Node) as INode);
const nextId =
n.nextSibling && mirror.getId((n.nextSibling as unknown) as INode);
const nextId = getNextId(n);
if (parentId === -1 || nextId === -1) {
return addQueue.push(n);
return addQueue.addNode(n);
}
this.adds.push({
parentId,
Expand Down Expand Up @@ -119,20 +208,42 @@ export default class MutationBuffer {
}
}

let candidate: DoubleLinkedListNode | null = null;
while (addQueue.length) {
if (
addQueue.every(
(n) => mirror.getId((n.parentNode as Node) as INode) === -1,
)
) {
let node: DoubleLinkedListNode | null = null;
if (candidate) {
const parentId = mirror.getId(
(candidate.value.parentNode as Node) as INode,
);
const nextId = getNextId(candidate.value);
if (parentId !== -1 && nextId !== -1) {
node = candidate;
}
}
if (!node) {
for (let index = addQueue.length - 1; index >= 0; index--) {
const _node = addQueue.get(index)!;
const parentId = mirror.getId(
(_node.value.parentNode as Node) as INode,
);
const nextId = getNextId(_node.value);
if (parentId !== -1 && nextId !== -1) {
node = _node;
break;
}
}
}
if (!node) {
/**
* If all nodes in queue could not find a serialized parent,
* it may be a bug or corner case. We need to escape the
* dead while loop at once.
*/
break;
}
pushAdd(addQueue.shift()!);
candidate = node.previous;
addQueue.removeNode(node.value);
pushAdd(node.value);
}

this.emit();
Expand Down
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"arrow-parens": false,
"only-arrow-functions": false,
"max-line-length": false,
"no-empty": false
"no-empty": false,
"max-classes-per-file": false
},
"rulesDirectory": []
}

0 comments on commit eb352be

Please sign in to comment.