site/node_modules/micromark-extension-gfm-table/lib/edit-map.js

204 lines
5.1 KiB
JavaScript
Raw Normal View History

2024-10-14 06:09:33 +00:00
/**
* @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, its 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, its 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
// }
// }
// }