Skip to content

Commit

Permalink
[diff++] Attempt to track moving of items
Browse files Browse the repository at this point in the history
  • Loading branch information
judofyr authored and rexxars committed Oct 6, 2020
1 parent 59a1bf6 commit 5becf1c
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {Transaction} from './types'

export type Meta = {chunk: Chunk; transactionIndex: number} | null

export type AnnotationExtractor = (value: Value<Meta>) => Annotation
export type AnnotationExtractor = {
fromValue(value: Value<Meta>): Annotation
fromMeta(meta: Meta): Annotation
}

class ArrayContentWrapper implements ArrayInput<Annotation> {
type: 'array' = 'array'
Expand Down Expand Up @@ -44,6 +47,11 @@ class ArrayContentWrapper implements ArrayInput<Annotation> {
this.extractor
))
}

annotationAt(idx: number): Annotation {
const meta = this.content.metas[idx]
return this.extractor.fromMeta(meta)
}
}

class ObjectContentWrapper implements ObjectInput<Annotation> {
Expand Down Expand Up @@ -131,7 +139,7 @@ class StringContentWrapper implements StringInput<Annotation> {
// eslint-disable-next-line max-depth
if (subEnd <= 0) break

push(part.value.slice(subStart, subEnd), this.extractor(part))
push(part.value.slice(subStart, subEnd), this.extractor.fromValue(part))
}

idx += length
Expand All @@ -146,7 +154,7 @@ function wrapValue(
raw: unknown,
extractor: AnnotationExtractor
): Input<Annotation> {
const annotation = extractor(value)
const annotation = extractor.fromValue(value)

if (value.content) {
switch (value.content.type) {
Expand All @@ -172,47 +180,38 @@ function wrapValue(
function extractAnnotationForFromInput(
timeline: Timeline,
firstChunk: Chunk | null,
value: Value<Meta>
meta: Meta
): Annotation {
let tx: Transaction | null = null
let chunk: Chunk | null = null

if (value.endMeta) {
if (meta) {
// The next transaction is where it disappeared:
const nextTxIndex = value.endMeta.transactionIndex + 1
tx = timeline.transactionByIndex(nextTxIndex)
if (!tx) return null

chunk = timeline.chunkByTransactionIndex(nextTxIndex, value.endMeta.chunk.index)
return annotationForTransactionIndex(timeline, meta.transactionIndex + 1, meta.chunk.index)
} else if (firstChunk) {
// Otherwise, it must have disappeared in the first transaction:
tx = timeline.transactionByIndex(firstChunk.start)
chunk = firstChunk
return annotationForTransactionIndex(timeline, firstChunk.start, firstChunk.index)
}

if (tx && chunk) {
return {
chunk,
timestamp: tx.timestamp,
author: tx.author
}
return null
}

function extractAnnotationForToInput(timeline: Timeline, meta: Meta): Annotation {
if (meta) {
return annotationForTransactionIndex(timeline, meta.transactionIndex, meta.chunk.index)
}

return null
}

function extractAnnotationForToInput(timeline: Timeline, value: Value<Meta>): Annotation {
if (value.startMeta) {
const tx = timeline.transactionByIndex(value.startMeta.transactionIndex)!
function annotationForTransactionIndex(timeline: Timeline, idx: number, chunkIdx?: number) {
const tx = timeline.transactionByIndex(idx)
if (!tx) return null

return {
chunk: value.startMeta.chunk,
timestamp: tx.timestamp,
author: tx.author
}
}
const chunk = timeline.chunkByTransactionIndex(idx, chunkIdx)
if (!chunk) return null

return null
return {
chunk,
timestamp: tx.timestamp,
author: tx.author
}
}

export function diffValue(
Expand All @@ -223,9 +222,22 @@ export function diffValue(
to: Value<Meta>,
toRaw: unknown
): Diff<Annotation> {
const fromInput = wrapValue(from, fromRaw, value =>
extractAnnotationForFromInput(timeline, firstChunk, value)
)
const toInput = wrapValue(to, toRaw, value => extractAnnotationForToInput(timeline, value))
const fromInput = wrapValue(from, fromRaw, {
fromValue(value) {
return extractAnnotationForFromInput(timeline, firstChunk, value.endMeta)
},
fromMeta(meta) {
return extractAnnotationForFromInput(timeline, firstChunk, meta)
}
})

const toInput = wrapValue(to, toRaw, {
fromValue(value) {
return extractAnnotationForToInput(timeline, value.startMeta)
},
fromMeta(meta) {
return extractAnnotationForToInput(timeline, meta)
}
})
return diffInput(fromInput, toInput)
}
24 changes: 16 additions & 8 deletions packages/@sanity/diff/src/calculate/diffArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ function diffExactByPosition<A>(
fromIndex: idx,
toIndex: idx,
hasMoved: false,
diff
diff,
annotation: toInput.annotationAt(idx)
})
}

Expand All @@ -117,7 +118,8 @@ function diffArrayByReinsert<A>(
fromIndex: undefined,
toIndex: idx,
hasMoved: false,
diff: addedInput(input, undefined, options)
diff: addedInput(input, undefined, options),
annotation: input.annotation
})
}

Expand All @@ -128,7 +130,8 @@ function diffArrayByReinsert<A>(
fromIndex: idx,
toIndex: undefined,
hasMoved: false,
diff: removedInput(input, undefined, options)
diff: removedInput(input, undefined, options),
annotation: input.annotation
})
}

Expand Down Expand Up @@ -162,7 +165,8 @@ function diffArrayByKey<A>(
fromIndex,
toIndex,
hasMoved,
diff
diff,
annotation: toArray.annotationAt(toIndex)
})

if (diff.isChanged || fromIndex !== toIndex) {
Expand Down Expand Up @@ -195,7 +199,8 @@ function diffArrayByKey<A>(
fromIndex,
toIndex: undefined,
hasMoved: false,
diff: removedInput(input, undefined, options)
diff: removedInput(input, undefined, options),
annotation: fromArray.annotationAt(fromIndex)
})

isChanged = true
Expand All @@ -209,7 +214,8 @@ function diffArrayByKey<A>(
fromIndex: undefined,
toIndex,
hasMoved: false,
diff: addedInput(input, undefined, options)
diff: addedInput(input, undefined, options),
annotation: toArray.annotationAt(toIndex)
})
}

Expand Down Expand Up @@ -343,7 +349,8 @@ export function removedArray<A>(
fromIndex: i,
toIndex: undefined,
hasMoved: false,
diff: removedInput(item, undefined, options)
diff: removedInput(item, undefined, options),
annotation: input.annotationAt(i)
})
}

Expand Down Expand Up @@ -373,7 +380,8 @@ export function addedArray<A>(
fromIndex: undefined,
toIndex: i,
hasMoved: false,
diff: addedInput(item, undefined, options)
diff: addedInput(item, undefined, options),
annotation: input.annotationAt(i)
})
}

Expand Down
4 changes: 4 additions & 0 deletions packages/@sanity/diff/src/inputWrappers/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ export default class ArrayWrapper<A> implements ArrayInput<A> {

return (this.elements[idx] = wrap(this.value[idx], this.annotation))
}

annotationAt(): A {
return this.annotation
}
}
2 changes: 2 additions & 0 deletions packages/@sanity/diff/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface ArrayInput<A> extends BaseInput<A> {
value: unknown[]
length: number
at(idx: number): Input<A>
annotationAt(idx: number): A
}

type AddedDiff<A, V> = {
Expand Down Expand Up @@ -147,4 +148,5 @@ export type ItemDiff<A> = {
toIndex: number | undefined
hasMoved: boolean
diff: Diff<A>
annotation: A
}
3 changes: 2 additions & 1 deletion packages/@sanity/field/src/diff/changes/buildChangeList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ export function buildArrayChangeList(
hasMoved: itemDiff.hasMoved,
toIndex: itemDiff.toIndex,
fromIndex: itemDiff.fromIndex,
annotation: itemDiff.diff.action === 'unchanged' ? undefined : itemDiff.diff.annotation
annotation:
itemDiff.diff.action === 'unchanged' ? itemDiff.annotation : itemDiff.diff.annotation
})

const attachItemDiff = (change: ChangeNode): ChangeNode => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ export function prepareDiffForPortableText(
fromValue: fromPseudoValue.children[0],
toValue: toPseudoValue.children[0]
},
annotation: null,
fromIndex: 0,
toIndex: 0,
hasMoved: false
Expand Down
22 changes: 17 additions & 5 deletions packages/mendoza/src/incremental-patcher.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/* eslint-disable default-case */
/* eslint-disable class-methods-use-this */
/* eslint-disable no-else-return */
/* eslint-disable no-lonely-if */
/* eslint-disable prefer-const */
import {ObjectModel} from './object-model'
import {RawPatch} from './patch'
import {Patcher} from './internal-patcher'
Expand Down Expand Up @@ -25,6 +30,7 @@ export type ObjectContent<T> = {
export type ArrayContent<T> = {
type: 'array'
elements: Value<T>[]
metas: T[]
}

export type StringContent<T> = {
Expand Down Expand Up @@ -71,7 +77,8 @@ class Model<T>
asArray(value: Value<T>): ArrayContent<T> {
if (!value.content) {
let elements = (value.data as unknown[]).map(item => this.wrapWithMeta(item, value.startMeta))
value.content = {type: 'array', elements}
let metas = elements.map(() => this.meta)
value.content = {type: 'array', elements, metas}
}

return value.content as ArrayContent<T>
Expand Down Expand Up @@ -185,11 +192,14 @@ class Model<T>
}

copyArray(value: Value<T> | null): ArrayContent<T> {
let elements = value ? this.asArray(value).elements : []
let arr = value ? this.asArray(value) : null
let elements = arr ? arr.elements : []
let metas = arr ? arr.metas : []

return {
type: 'array',
elements
elements,
metas
}
}

Expand All @@ -203,11 +213,13 @@ class Model<T>

arrayAppendValue(target: ArrayContent<T>, value: Value<T>): void {
target.elements.push(value)
target.metas.push(this.meta)
}

arrayAppendSlice(target: ArrayContent<T>, source: Value<T>, left: number, right: number): void {
let slice = this.asArray(source).elements.slice(left, right)
target.elements.push(...slice)
let arr = this.asArray(source)
target.elements.push(...arr.elements.slice(left, right))
target.metas.push(...arr.metas.slice(left, right))
}

stringAppendValue(target: StringContent<T>, value: Value<T>): void {
Expand Down

0 comments on commit 5becf1c

Please sign in to comment.