2023-01-13 07:42:29 -05:00
|
|
|
/* eslint-disable
|
|
|
|
no-return-assign,
|
|
|
|
*/
|
|
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
// Fix any style issues and re-enable lint.
|
|
|
|
/*
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
* DS101: Remove unnecessary use of Array.from
|
|
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
* DS207: Consider shorter variations of null checks
|
|
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
|
*/
|
|
|
|
import Core from 'overleaf-editor-core'
|
|
|
|
import logger from '@overleaf/logger'
|
|
|
|
import * as Errors from './Errors.js'
|
|
|
|
|
|
|
|
const { MoveFileOperation, AddFileOperation, EditFileOperation } = Core
|
|
|
|
|
|
|
|
export function buildDiff(chunk, fromVersion, toVersion) {
|
|
|
|
chunk = Core.Chunk.fromRaw(chunk.chunk)
|
|
|
|
const chunkStartVersion = chunk.getStartVersion()
|
|
|
|
|
|
|
|
const diff = _getInitialDiffSnapshot(chunk, fromVersion)
|
|
|
|
|
|
|
|
const changes = chunk
|
|
|
|
.getChanges()
|
|
|
|
.slice(fromVersion - chunkStartVersion, toVersion - chunkStartVersion)
|
|
|
|
for (let i = 0; i < changes.length; i++) {
|
|
|
|
const change = changes[i]
|
|
|
|
for (const operation of Array.from(change.getOperations())) {
|
|
|
|
if (operation.pathname === null || operation.pathname === '') {
|
|
|
|
// skip operations for missing files
|
|
|
|
logger.warn({ diff, operation }, 'invalid pathname in operation')
|
|
|
|
} else if (operation instanceof EditFileOperation) {
|
|
|
|
_applyEditFileToDiff(diff, operation)
|
|
|
|
} else if (operation instanceof AddFileOperation) {
|
|
|
|
_applyAddFileToDiff(diff, operation)
|
|
|
|
} else if (operation instanceof MoveFileOperation) {
|
|
|
|
if (operation.isRemoveFile()) {
|
|
|
|
const deletedAtV = fromVersion + i
|
|
|
|
_applyDeleteFileToDiff(diff, operation, deletedAtV)
|
|
|
|
} else {
|
|
|
|
_applyMoveFileToDiff(diff, operation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Object.values(diff)
|
|
|
|
}
|
|
|
|
|
|
|
|
function _getInitialDiffSnapshot(chunk, fromVersion) {
|
|
|
|
// Start with a 'diff' which is snapshot of the filetree at the beginning,
|
|
|
|
// with nothing in the diff marked as changed.
|
|
|
|
// Use a bare object to protect against reserved names.
|
|
|
|
const diff = Object.create(null)
|
2023-05-25 06:20:57 -04:00
|
|
|
const files = _getInitialFiles(chunk, fromVersion)
|
|
|
|
for (const [pathname, file] of Object.entries(files)) {
|
|
|
|
diff[pathname] = { pathname, editable: file.isEditable() }
|
2023-01-13 07:42:29 -05:00
|
|
|
}
|
|
|
|
return diff
|
|
|
|
}
|
|
|
|
|
2023-05-25 06:20:57 -04:00
|
|
|
function _getInitialFiles(chunk, fromVersion) {
|
2023-01-13 07:42:29 -05:00
|
|
|
const snapshot = chunk.getSnapshot()
|
|
|
|
const changes = chunk
|
|
|
|
.getChanges()
|
|
|
|
.slice(0, fromVersion - chunk.getStartVersion())
|
|
|
|
snapshot.applyAll(changes)
|
2023-05-25 06:20:57 -04:00
|
|
|
return snapshot.fileMap.files
|
2023-01-13 07:42:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function _applyAddFileToDiff(diff, operation) {
|
2023-08-16 05:50:54 -04:00
|
|
|
return (diff[operation.pathname] = {
|
|
|
|
pathname: operation.pathname,
|
|
|
|
operation: 'added',
|
|
|
|
editable: operation.file.isEditable(),
|
|
|
|
})
|
2023-01-13 07:42:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function _applyEditFileToDiff(diff, operation) {
|
|
|
|
const change = diff[operation.pathname]
|
|
|
|
if ((change != null ? change.operation : undefined) == null) {
|
|
|
|
// avoid exception for non-existent change
|
|
|
|
return (diff[operation.pathname] = {
|
|
|
|
pathname: operation.pathname,
|
|
|
|
operation: 'edited',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function _applyMoveFileToDiff(diff, operation) {
|
|
|
|
if (
|
|
|
|
diff[operation.newPathname] != null &&
|
|
|
|
diff[operation.newPathname].operation !== 'removed'
|
|
|
|
) {
|
|
|
|
const err = new Errors.InconsistentChunkError(
|
|
|
|
'trying to move to file that already exists',
|
|
|
|
{ diff, operation }
|
|
|
|
)
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
const change = diff[operation.pathname]
|
|
|
|
if (change == null) {
|
|
|
|
logger.warn({ diff, operation }, 'tried to rename non-existent file')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
change.newPathname = operation.newPathname
|
|
|
|
if (change.operation === 'added') {
|
|
|
|
// If this file was added this time, just leave it as an add, but
|
|
|
|
// at the new name.
|
|
|
|
change.pathname = operation.newPathname
|
|
|
|
delete change.newPathname
|
|
|
|
} else {
|
|
|
|
change.operation = 'renamed'
|
|
|
|
}
|
|
|
|
diff[operation.newPathname] = change
|
|
|
|
return delete diff[operation.pathname]
|
|
|
|
}
|
|
|
|
|
|
|
|
function _applyDeleteFileToDiff(diff, operation, deletedAtV) {
|
|
|
|
// avoid exception for non-existent change
|
|
|
|
if (diff[operation.pathname] != null) {
|
|
|
|
diff[operation.pathname].operation = 'removed'
|
|
|
|
}
|
|
|
|
return diff[operation.pathname] != null
|
|
|
|
? (diff[operation.pathname].deletedAtV = deletedAtV)
|
|
|
|
: undefined
|
|
|
|
}
|