site/node_modules/micromark-extension-gfm-table/lib/edit-map.js
2024-10-14 08:09:33 +02:00

204 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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
// }
// }
// }