mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-03 13:19:29 +08:00
71 lines
2.2 KiB
Text
71 lines
2.2 KiB
Text
import * as regex from '../lib/regex'
|
|
|
|
/**
|
|
* This splits a string on a top-level character.
|
|
*
|
|
* Regex doesn't support recursion (at least not the JS-flavored version).
|
|
* So we have to use a tiny state machine to keep track of paren placement.
|
|
*
|
|
* Expected behavior using commas:
|
|
* var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
|
|
* ─┬─ ┬ ┬ ┬
|
|
* x x x ╰──────── Split because top-level
|
|
* ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
|
|
*
|
|
* @param {string} input
|
|
* @param {string} separator
|
|
*/
|
|
export function* splitAtTopLevelOnly(input, separator) {
|
|
let SPECIALS = new RegExp(`[(){}\\[\\]${regex.escape(separator)}]`, 'g')
|
|
|
|
let depth = 0
|
|
let lastIndex = 0
|
|
let found = false
|
|
let separatorIndex = 0
|
|
let separatorStart = 0
|
|
let separatorLength = separator.length
|
|
|
|
// Find all paren-like things & character
|
|
// And only split on commas if they're top-level
|
|
for (let match of input.matchAll(SPECIALS)) {
|
|
let matchesSeparator = match[0] === separator[separatorIndex]
|
|
let atEndOfSeparator = separatorIndex === separatorLength - 1
|
|
let matchesFullSeparator = matchesSeparator && atEndOfSeparator
|
|
|
|
if (match[0] === '(') depth++
|
|
if (match[0] === ')') depth--
|
|
if (match[0] === '[') depth++
|
|
if (match[0] === ']') depth--
|
|
if (match[0] === '{') depth++
|
|
if (match[0] === '}') depth--
|
|
|
|
if (matchesSeparator && depth === 0) {
|
|
if (separatorStart === 0) {
|
|
separatorStart = match.index
|
|
}
|
|
|
|
separatorIndex++
|
|
}
|
|
|
|
if (matchesFullSeparator && depth === 0) {
|
|
found = true
|
|
|
|
yield input.substring(lastIndex, separatorStart)
|
|
lastIndex = separatorStart + separatorLength
|
|
}
|
|
|
|
if (separatorIndex === separatorLength) {
|
|
separatorIndex = 0
|
|
separatorStart = 0
|
|
}
|
|
}
|
|
|
|
// Provide the last segment of the string if available
|
|
// Otherwise the whole string since no `char`s were found
|
|
// This mirrors the behavior of string.split()
|
|
if (found) {
|
|
yield input.substring(lastIndex)
|
|
} else {
|
|
yield input
|
|
}
|
|
}
|