am close...

pull/1274/head
Rich-Harris 8 years ago
parent 5349184e75
commit 7c953a6622

3
.gitignore vendored

@ -17,4 +17,5 @@ src/generators/dom/shared.ts
package-lock.json
.idea/
*.iml
store.umd.js
store.umd.js
yarn-error.log

@ -0,0 +1,81 @@
# List diffing
## [ABCDE] -> [ABCD]
* o = 5, n = 4. Compare E and D
E is not present in new list, so remove. o--
* o = 4, n = 4. Compare D and D
same, so continue (repeat)
## [ABCD] -> [ABCDE]
* o = 4, n = 5. Compare D and E
E is not present in old list, so create/insert. n--
* o = 4, n = 4. Compare D and D
same, so continue (repeat)
## [ABCDE] -> [BCDE]
* o = 5, n = 4. Compare E and E, D and D, C and C, B and B
* o = 1, n = 0. A is left over, so remove
## [BCDE] -> [ABCDE]
* o = 4, n = 5. Compare E and E, D and D, C and C, B and B
* o = 0, n = 1. insert A
## [ABCDEFGHIJKL] -> [ABCGHIDEFJKL]
* o = 12, n = 12. Compare J, K, L
* o = 9, n = 9. Compare I and F
* delta is 3 for both. insertion wins. insert F. mark as moved. n--
* o = 9, n = 8. Compare I and E
* delta is 3 for both. insertion wins. insert E. mark as moved. n--
* o = 9, n = 7. Compare I and D
* delta is 3 for both. insertion wins. insert D. mark as moved. n--
* o = 9, n = 6. Compare I and I. Repeat
* o = 6, n = 3. Compare F and C. F was moved, o--
* o = 5, n = 3. Compare E and C. E was moved, o--
* o = 4, n = 3. Compare D and C. D was moved, o--
* o = 3, n = 3. Compare C and C. Repeat
## [ABCDEFG] -> [AFCDEBG]
* o = 7, n = 7. Compare G and G
* o = 6, n = 6. Compare F and B
* both exist. in both cases, delta is 4. insertion wins. insert B. mark B as moved. n--
* o = 6, n = 5. Compare F and E
* both exist. delta(F) is 4, delta(E) is 0, so we put F into a side pile. o--
* o = 5, n = 5. E/E. Repeat
* o = 2, n = 2. Compare B and F
* F is in side pile, so insert. n--
* o = 2, n = 1. Compare B and A
* B was displaced. o--
* o = 1, n = 1. Compare A and A
## [ABCDEFGHIJKL] -> [IHGFED]
* o = 12, n = 6. Compare L and D. L is removed, o--
* o = 11, n = 6. Compare K and D. K is removed, o--
* o = 10, n = 6. Compare J and D. J is removed, o--
* o = 9, n = 6. Compare I and D. delta(I) is 8, delta(D) is 2. put I in side pile. o--
* o = 8, n = 6. Compare H and D. delta(H) is 6, delta(D) is 2. put H in side pile. o--
* o = 7, n = 6. Compare G and D. delta(G) is 4, delta(D) is 2. put G in side pile. o--
* o = 6, n = 6. Compare F and D. delta(F) is 2, delta(D) is 2. insertion wins, mark D as moved. n--
* o = 6, n = 5. Compare F and E. delta(F) is 2, delta(E) is 0. put F in side pile. o--
* o = 5, n = 5. Compare E and E. o--, n--
* o = 4, n = 4. Compare D and F. F is in side pile, so insert. n--
* o = 4, n = 3. Compare D and G. G is in side pile, so insert. n--
* o = 4, n = 2. Compare D and H. H is in side pile, so insert. n--
* o = 4, n = 1. Compare D and I. I is in side pile. so insert. n--
* o = 3, n = 0. Remove remaining old items
## [FABED] -> [BEADF]
* [FABED] o = 5, n = 5. Compare D and F. delta(F) > delta(D), so we move F, and mark as such. n--
* [ABEDF] o = 5, n = 4. Compare D and D. o--, n--
* [ABEDF] o = 4, n = 3. Compare E and A. delta(E) > delta(A), so we mark E as will_move. o--
* [ABEDF] o = 3, n = 3. Compare B and A. delta(B) > delta(A), so we mark B as will_move. o--
* [ABEDF] o = 2, n = 3. Compare A and A. o--. n--
* [ABEDF] o = 1, n = 2. Compare F and E. F was marked as moved, o--
* [ABEDF] o = 0, n = 2. Insert E then B from will_move

@ -17,9 +17,9 @@ fs.readdirSync(__dirname).forEach(file => {
if (node.type !== 'ExportNamedDeclaration') return;
// check no ES6+ slipped in
acorn.parse(source.slice(node.declaration.start, node.end), {
ecmaVersion: 5
});
// acorn.parse(source.slice(node.declaration.start, node.end), {
// ecmaVersion: 5
// });
const declaration = node.declaration;
if (!declaration) return;

@ -1,7 +1,7 @@
import { assign } from './utils.js';
export function destroyIteration(iteration, lookup) {
var first = iteration.first
var first = iteration.first;
if (first && first.parentNode) {
iteration.u();
}
@ -18,80 +18,136 @@ export function outroAndDestroyIteration(iteration, lookup) {
}
export function updateKeyedEach(component, key, changed, key_prop, dynamic, list, head, lookup, node, has_outro, create_each_block, intro_method, get_context) {
var expected = head;
var old_indexes = {};
var i = 0;
var old_keys = [];
while (head) {
old_keys.push(head.key);
old_indexes[head.key] = i++;
head = head.next;
}
var new_keys = list.map(item => item[key_prop]).join(''); // TODO this is temporary
var keep = {};
var should_remount = {};
var o = old_keys.length;
var n = list.length;
for (var i = 0; i < list.length; i += 1) {
var new_blocks = {};
var deltas = {};
var i = n;
while (i--) {
var key = list[i][key_prop];
var iteration = lookup[key];
if (dynamic && iteration) iteration.p(changed, get_context(i));
if (expected && (key === expected.key)) {
var first = iteration && iteration.first;
var parentNode = first && first.parentNode;
var next_item = list[i + 1];
var next = next_item && lookup[next_item[key_prop]];
if (!parentNode || (iteration && iteration.next) != next) should_remount[key] = 1;
expected = iteration.next;
} else if (iteration) {
should_remount[key] = 1;
expected = iteration.next;
} else {
// key is being inserted
iteration = lookup[key] = create_each_block(component, key, get_context(i));
iteration.c();
should_remount[key] = 1;
var block = lookup[key];
if (!block) {
block = create_each_block(component, key, get_context(i));
block.c();
} else if (dynamic) {
// TODO update
}
lookup[key] = iteration;
keep[iteration.key] = 1;
}
var destroy = has_outro
? outroAndDestroyIteration
: destroyIteration;
new_blocks[key] = block;
iteration = head;
while (iteration) {
if (!keep[iteration.key]) destroy(iteration, lookup);
iteration = iteration.next;
if (key in old_indexes) deltas[key] = Math.abs(i - old_indexes[key]);
}
var next = null;
var next_iteration = null;
for (i = list.length - 1; i >= 0; i -= 1) {
var data = list[i];
var key = data[key_prop];
iteration = lookup[key];
if (key in should_remount) {
var anchor;
if (has_outro) {
var next_key = next && next.key;
var neighbour = iteration.next;
var anchor_key;
while (neighbour && anchor_key != next_key && !keep[anchor_key]) {
anchor = neighbour && neighbour.first;
neighbour = neighbour.next;
anchor_key = neighbour && neighbour.key;
}
var will_move = {};
var did_move = {};
var r = 100;
console.log('deltas', deltas);
while (o && n) {
if (!--r) throw new Error('hmm');
var item = list[n - 1];
var new_key = item[key_prop];
var old_key = old_keys[o - 1];
console.log(`${old_keys.slice(0, o - 1).join('')}[${old_key}]${old_keys.slice(o).join('')} ${new_keys.slice(0, n - 1)}[${new_key}]${new_keys.slice(n)}`);
if (new_key === old_key) {
console.log('SAME SAME');
o--;
n--;
next = new_blocks[new_key];
}
else if (lookup[old_key] && !new_blocks[old_key]) {
// removing
console.log(`removing ${old_key}`);
destroyIteration(lookup[old_key], lookup);
o--;
}
else if (!lookup[new_key]) {
// creating
console.log(`adding ${new_key}`);
new_blocks[new_key][intro_method](node, next && next.first);
next = new_blocks[new_key];
lookup[new_key] = new_blocks[new_key];
n--;
}
else if (lookup[old_key] && lookup[new_key]) {
console.log('both previously existed');
if (did_move[old_key]) {
console.log('did move', old_key);
o--;
// next = new_blocks[old_key];
} else if (will_move[new_key]) {
console.log('moving', new_key);
new_blocks[new_key][intro_method](node, next && next.first);
n--;
} else if (deltas[new_key] > deltas[old_key]) {
// we already have both blocks, but they're out of order
console.log('inserting', new_key);
new_blocks[new_key][intro_method](node, next && next.first);
next = new_blocks[new_key];
did_move[new_key] = true;
n--;
} else {
anchor = next_iteration && next_iteration.first;
console.log('will move', old_key);
will_move[old_key] = true;
o--;
}
}
iteration[intro_method](node, anchor);
else {
throw new Error('???');
}
iteration.next = next_iteration;
if (next_iteration) next_iteration.last = iteration;
next_iteration = iteration;
console.log(document.body.textContent);
console.log(`next is ${next && next.key}\n`);
}
console.log({ will_move: Object.keys(will_move) });
while (o--) {
var old_key = old_keys[o];
if (!new_blocks[old_key]) destroyIteration(lookup[old_key], lookup);
}
while (n--) {
var key = list[n][key_prop];
new_blocks[key][intro_method](node, next && next.first);
next = new_blocks[key];
}
// TODO keep track of keys, so this is unnecessary
var next = null;
var i = list.length;
while (i--) {
var key = list[i][key_prop];
var block = lookup[key] = new_blocks[key];
block.next = next;
next = block;
}
}

@ -18,43 +18,52 @@ function permute() {
}
export default {
solo: true,
allowES2015: true,
data: {
values: toObjects('abc'),
values: toObjects('duqbmineapjhtlofrskcg'),
},
html: `(a)(b)(c)`,
// html: `(a)(b)(c)`,
test(assert, component, target) {
function test(sequence) {
const previous = target.textContent;
console.group(`${previous.replace(/[()]/g, '')} -> ${sequence}`);
const expected = sequence.split('').map(x => `(${x})`).join('');
component.set({ values: toObjects(sequence) });
console.log(`result: ${target.textContent.replace(/[()]/g, '')}`);
assert.htmlEqual(
target.innerHTML,
expected,
`\n${previous} -> ${expected}\n${target.textContent}`
);
console.groupEnd();
}
// first, some fixed tests so that we can debug them
test('abc');
test('abcd');
test('abecd');
test('fabecd');
test('fabed');
test('beadf');
test('ghbeadf');
test('gf');
test('gc');
test('g');
test('');
test('abc');
test('duqbmineapjhtlofrskcg');
// test('abc');
// test('abcd');
// test('abecd');
// test('fabecd');
// test('fabed');
// test('beadf');
// test('ghbeadf');
// test('gf');
// test('gc');
// test('g');
// test('');
// test('abc');
// test('duqbmineapjhtlofrskcg');
test('hdnkjougmrvftewsqpailcb');
test('bidhfacge');
test('kgjnempcboaflidh');
test('fekbijachgd');
test('kdmlgfbicheja');
return;
// then, we party
for (let i = 0; i < 100; i += 1) test(permute());
for (let i = 0; i < 1000; i += 1) test(permute());
}
};

Loading…
Cancel
Save