/** * @typedef {import('micromark-util-types').Event} Event */ // Port of `edit_map.rs` from `markdown-rs`. // This should move to `markdown-js` later. // Deal with several changes in events, batching them together. // // Preferably, changes should be kept to a minimum. // Sometimes, it’s needed to change the list of events, because parsing can be // messy, and it helps to expose a cleaner interface of events to the compiler // and other users. // It can also help to merge many adjacent similar events. // And, in other cases, it’s needed to parse subcontent: pass some events // through another tokenizer and inject the result. /** * @typedef {[number, number, Array]} Change * @typedef {[number, number, number]} Jump */ /** * Tracks a bunch of edits. */ export class EditMap { /** * Create a new edit map. */ constructor() { /** * Record of changes. * * @type {Array} */ this.map = [] } /** * Create an edit: a remove and/or add at a certain place. * * @param {number} index * @param {number} remove * @param {Array} add * @returns {undefined} */ add(index, remove, add) { addImpl(this, index, remove, add) } // To do: add this when moving to `micromark`. // /** // * Create an edit: but insert `add` before existing additions. // * // * @param {number} index // * @param {number} remove // * @param {Array} add // * @returns {undefined} // */ // addBefore(index, remove, add) { // addImpl(this, index, remove, add, true) // } /** * Done, change the events. * * @param {Array} events * @returns {undefined} */ consume(events) { this.map.sort(function (a, b) { return a[0] - b[0] }) /* c8 ignore next 3 -- `resolve` is never called without tables, so without edits. */ if (this.map.length === 0) { return } // To do: if links are added in events, like they are in `markdown-rs`, // this is needed. // // Calculate jumps: where items in the current list move to. // /** @type {Array} */ // const jumps = [] // let index = 0 // let addAcc = 0 // let removeAcc = 0 // while (index < this.map.length) { // const [at, remove, add] = this.map[index] // removeAcc += remove // addAcc += add.length // jumps.push([at, removeAcc, addAcc]) // index += 1 // } // // . shiftLinks(events, jumps) let index = this.map.length /** @type {Array>} */ const vecs = [] while (index > 0) { index -= 1 vecs.push( events.slice(this.map[index][0] + this.map[index][1]), this.map[index][2] ) // Truncate rest. events.length = this.map[index][0] } vecs.push([...events]) events.length = 0 let slice = vecs.pop() while (slice) { events.push(...slice) slice = vecs.pop() } // Truncate everything. this.map.length = 0 } } /** * Create an edit. * * @param {EditMap} editMap * @param {number} at * @param {number} remove * @param {Array} add * @returns {undefined} */ function addImpl(editMap, at, remove, add) { let index = 0 /* c8 ignore next 3 -- `resolve` is never called without tables, so without edits. */ if (remove === 0 && add.length === 0) { return } while (index < editMap.map.length) { if (editMap.map[index][0] === at) { editMap.map[index][1] += remove // To do: before not used by tables, use when moving to micromark. // if (before) { // add.push(...editMap.map[index][2]) // editMap.map[index][2] = add // } else { editMap.map[index][2].push(...add) // } return } index += 1 } editMap.map.push([at, remove, add]) } // /** // * Shift `previous` and `next` links according to `jumps`. // * // * This fixes links in case there are events removed or added between them. // * // * @param {Array} events // * @param {Array} jumps // */ // function shiftLinks(events, jumps) { // let jumpIndex = 0 // let index = 0 // let add = 0 // let rm = 0 // while (index < events.length) { // const rmCurr = rm // while (jumpIndex < jumps.length && jumps[jumpIndex][0] <= index) { // add = jumps[jumpIndex][2] // rm = jumps[jumpIndex][1] // jumpIndex += 1 // } // // Ignore items that will be removed. // if (rm > rmCurr) { // index += rm - rmCurr // } else { // // ? // // if let Some(link) = &events[index].link { // // if let Some(next) = link.next { // // events[next].link.as_mut().unwrap().previous = Some(index + add - rm); // // while jumpIndex < jumps.len() && jumps[jumpIndex].0 <= next { // // add = jumps[jumpIndex].2; // // rm = jumps[jumpIndex].1; // // jumpIndex += 1; // // } // // events[index].link.as_mut().unwrap().next = Some(next + add - rm); // // index = next; // // continue; // // } // // } // index += 1 // } // } // }