Check if a transaction overwrites the entire document

Hi! I have a transaction filter that rejects the transaction if the first or last line is modified. I need to add a check to ensure that if the entire document is being overwritten (i.e., view.dispatch({changes: {from: 0, to: view.state.doc.length, insert: code}})), the rejection doesn’t occur. I quickly came up with this solution, which seems to work:

function preventFirstLastLineDeletion() {
    return EditorState.transactionFilter.of(tr => {
        if (!tr.docChanged)
            return tr;

        const isEntireDoc = tr.changes.sections[0] === tr.startState.doc.length;
        if (isEntireDoc)
            return tr;

        // ... snip

        return tr;
    });
}

Is there a better way to achieve this? changes.sections doesn’t seem to be documented.

Spoiler: here’s the // ... snip part of the code:

Show it
        // Check if the transaction modifies the first or last line
        const {doc} = tr.startState;
        const lineCount = doc.lines;
        const firstLine = doc.line(1);
        const lastLine = lineCount > 1 ? doc.line(lineCount) : null;

        const modifiedFirst = tr.changes.touchesRange(firstLine.from, firstLine.to);
        const modifiedLast = lastLine && tr.changes.touchesRange(lastLine.from, lastLine.to);
        // If the transaction modifies the firstLine or last line, reject it
        if (modifiedFirst || modifiedLast) {
            return null;
        }

ChangeSet.sections isn’t part of the public interface, so I’d recommend against touching it. You could instead check that iterGaps doesn’t find any gaps.

Thank you for the suggestion. I was able to modify the code based on your tip, and it works perfectly now (I think). Here’s the final code:

function preventFirstLastLineDeletion() {
    return EditorState.transactionFilter.of(tr => {
        if (!tr.docChanged)
            return tr;

        const entireDocChanged = getNumUnchangedRanges(tr.changes) === 0;
        if (entireDocChanged)
            return tr;

        ...

        return tr;
    });
}

function getNumUnchangedRanges(changes) {
    let numRanges = 0;
    changes.iterGaps((/*posA, posB, length*/) => {
        numRanges += 1;
    });
    return numRanges;
}