204 lines
5.1 KiB
JavaScript
204 lines
5.1 KiB
JavaScript
|
/**
|
|||
|
* @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<Event>]} 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<Change>}
|
|||
|
*/
|
|||
|
this.map = []
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Create an edit: a remove and/or add at a certain place.
|
|||
|
*
|
|||
|
* @param {number} index
|
|||
|
* @param {number} remove
|
|||
|
* @param {Array<Event>} 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<Event>} add
|
|||
|
// * @returns {undefined}
|
|||
|
// */
|
|||
|
// addBefore(index, remove, add) {
|
|||
|
// addImpl(this, index, remove, add, true)
|
|||
|
// }
|
|||
|
|
|||
|
/**
|
|||
|
* Done, change the events.
|
|||
|
*
|
|||
|
* @param {Array<Event>} 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<Jump>} */
|
|||
|
// 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<Array<Event>>} */
|
|||
|
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<Event>} 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<Event>} events
|
|||
|
// * @param {Array<Jump>} 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
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|