UNPKG

balanced-pairs

Version:

🏄 get blocks of balanced pairs, eg: {} \<a>\</a> or code fences ```

233 lines (193 loc) 4.54 kB
const superSplit = require('super-split') const pointInPolygon = require('point-in-polygon') const block = (start, depth) => { const t = { start, end: null, depth, pre: '', body: '', post: '', root: false, updateBody: newBody => { t.newBody = newBody }, delimiter: {}, children: [] } return t } const deepFold = (block, opts, depth = 1) => { const blockStrs = block.children.map(block => { return block.delimiter.body }) const subs = superSplit(block.body, blockStrs) if (typeof subs === 'string') { if (block.newBody) { return block.newBody } return block.delimiter.body } let out = [] subs.forEach(sub => { const child = block.children.find(child => { if (child.delimiter.body === sub) { return true } return false }) if (child === undefined) { out.push(sub) } else { const subFold = deepFold(child, opts, depth += 1) out.push(subFold) } }) if (block.depth === 0) { out = out.join('') } else { out = `{${out.join('')}}` } if (block.newBody) { return block.newBody } return out } module.exports = (content, opts) => { const delims = [opts.open, opts.close] const particle = superSplit(content, delims) const openLen = opts.open.length const closeLen = opts.close.length const evenDelims = opts.open === opts.close const result = { blocks: [], polygon: [], mode: evenDelims ? 'even' : 'odd', inside: n => { if (result.mode === 'even') { return pointInPolygon([n, 0], result.polygon) } const insideOf = [] result.list.forEach(block => { if (n >= block.start && n <= block.end) { insideOf.push(block) } }) return insideOf }, flatten: () => { const tree = result.blocks.slice()[0] return deepFold(tree, opts) } } // Even Delims, ie: "```code``` fence ```code```" let run = [] let n = 0 let on = false // Only used with evenDelims // Off Delims, ie: `foo {bar {baz} quz} spkr}` let openDepth = 0 const stack = [] const currentItem = { start: 0, end: content.length - 1, depth: 0, body: content, root: true, children: [] } stack.push(currentItem) // Map of blocks by level const levels = [[]] // Add the root block to the levels array levels[0].push(currentItem) // List of blocks by close-order const list = [] particle.forEach((atom, i) => { if (evenDelims === false) { const currentItem = stack[stack.length - 1] || undefined if (atom === opts.open) { openDepth += 1 if (!levels[openDepth]) { levels[openDepth] = [] } const start = n + openLen const nextItem = block(start, openDepth) if (currentItem) { currentItem.children.push(nextItem) } stack.push(nextItem) } if (atom === opts.close && currentItem.depth > 0) { openDepth -= 1 const out = n if (currentItem) { const start = currentItem.start const body = content.slice(start, out) const end = out - 1 currentItem.end = end currentItem.pre = content.slice(0, start) currentItem.body = body currentItem.post = content.slice(end + 1) const delim = currentItem.delimiter delim.start = start - openLen delim.end = end + closeLen delim.pre = content.slice(0, delim.start) delim.body = opts.open + body + opts.close delim.post = content.slice(delim.end + 1) const item = stack.pop() levels[openDepth + 1].push(item) list.push(item) } } n += atom.length } if (evenDelims) { n += atom.length if (!delims.includes(atom)) { return } on = !on const n2 = on ? n : n - closeLen run.push(n2) if (run.length === 2) { const start = run[0] const end = run[1] - 1 // The polygon end is offset due to the // way the PIP algorithm defines corners const polygon = [ [start, -1], [end + 1, -1], [end + 1, 1], [start, 1], // Close poly [start, -1] ] const body = particle[i - 1] const block = { start, end, pre: content.slice(0, start), body, post: content.slice(end + 1), polygon, delimiter: { start: start - openLen, end: end + closeLen, pre: content.slice(0, start - openLen), body: opts.open + body + opts.close, post: content.slice(end + 1) } } result.blocks.push(block) result.polygon = result.polygon.concat(polygon) run = [] } } }) if (!evenDelims) { result.blocks = stack result.levels = levels result.list = list } return result }